Skip to content

Commit 5ff60cc

Browse files
committed
feat: add storage settings support
1 parent 0209b09 commit 5ff60cc

File tree

4 files changed

+316
-5
lines changed

4 files changed

+316
-5
lines changed

docs/resources/settings.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ resource "supabase_settings" "production" {
3939
mfa_phone_otp_length = 6
4040
sms_otp_length = 6
4141
})
42+
43+
storage = jsonencode({
44+
# fileSizeLimit is expressed in bytes (e.g., 50MB = 50 * 1024 * 1024)
45+
fileSizeLimit = 52428800
46+
features = {
47+
imageTransformation = {
48+
enabled = true
49+
}
50+
s3Protocol = {
51+
enabled = false
52+
}
53+
}
54+
})
4255
}
4356
```
4457

examples/resources/supabase_settings/resource.tf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,17 @@ resource "supabase_settings" "production" {
2424
mfa_phone_otp_length = 6
2525
sms_otp_length = 6
2626
})
27+
28+
storage = jsonencode({
29+
# fileSizeLimit is expressed in bytes (e.g., 50MB = 50 * 1024 * 1024)
30+
fileSizeLimit = 52428800
31+
features = {
32+
imageTransformation = {
33+
enabled = true
34+
}
35+
s3Protocol = {
36+
enabled = false
37+
}
38+
}
39+
})
2740
}

internal/provider/settings_resource.go

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ func (r *SettingsResource) Create(ctx context.Context, req resource.CreateReques
145145
if !data.Auth.IsNull() {
146146
resp.Diagnostics.Append(updateAuthConfig(ctx, &data, r.client)...)
147147
}
148+
if !data.Storage.IsNull() {
149+
resp.Diagnostics.Append(updateStorageConfig(ctx, &data, r.client)...)
150+
}
148151
// TODO: update all settings above concurrently
149152
if resp.Diagnostics.HasError() {
150153
return
@@ -183,6 +186,9 @@ func (r *SettingsResource) Read(ctx context.Context, req resource.ReadRequest, r
183186
if !data.Auth.IsNull() {
184187
resp.Diagnostics.Append(readAuthConfig(ctx, &data, r.client)...)
185188
}
189+
if !data.Storage.IsNull() {
190+
resp.Diagnostics.Append(readStorageConfig(ctx, &data, r.client)...)
191+
}
186192
// TODO: read all settings above concurrently
187193
if resp.Diagnostics.HasError() {
188194
return
@@ -223,6 +229,9 @@ func (r *SettingsResource) Update(ctx context.Context, req resource.UpdateReques
223229
if !planData.Auth.IsNull() && !planData.Auth.Equal(stateData.Auth) {
224230
resp.Diagnostics.Append(updateAuthConfig(ctx, &planData, r.client)...)
225231
}
232+
if !planData.Storage.IsNull() && !planData.Storage.Equal(stateData.Storage) {
233+
resp.Diagnostics.Append(updateStorageConfig(ctx, &planData, r.client)...)
234+
}
226235
// TODO: update all settings above concurrently
227236
if resp.Diagnostics.HasError() {
228237
return
@@ -253,6 +262,7 @@ func (r *SettingsResource) ImportState(ctx context.Context, req resource.ImportS
253262
resp.Diagnostics.Append(readNetworkConfig(ctx, &data, r.client)...)
254263
resp.Diagnostics.Append(readApiConfig(ctx, &data, r.client)...)
255264
resp.Diagnostics.Append(readAuthConfig(ctx, &data, r.client)...)
265+
resp.Diagnostics.Append(readStorageConfig(ctx, &data, r.client)...)
256266

257267
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
258268
}
@@ -432,9 +442,25 @@ func pickConfig(source any, target map[string]any) {
432442
tag := t.Field(i).Tag.Get("json")
433443
k := strings.Split(tag, ",")[0]
434444
// Check that tag is picked by target
435-
if _, ok := target[k]; ok {
436-
target[k] = v.Field(i).Interface()
445+
targetVal, ok := target[k]
446+
if !ok {
447+
continue
448+
}
449+
sourceField := v.Field(i)
450+
// Recursively merge nested structs so user values survive when API omits fields.
451+
if targetMap, isMap := targetVal.(map[string]any); isMap {
452+
if sourceField.Kind() == reflect.Pointer {
453+
if sourceField.IsNil() {
454+
continue
455+
}
456+
sourceField = sourceField.Elem()
457+
}
458+
if sourceField.Kind() == reflect.Struct {
459+
pickConfig(sourceField.Interface(), targetMap)
460+
continue
461+
}
437462
}
463+
target[k] = sourceField.Interface()
438464
}
439465
}
440466

@@ -615,3 +641,52 @@ func updateNetworkConfig(ctx context.Context, plan *SettingsResourceModel, clien
615641
}
616642
return nil
617643
}
644+
645+
func readStorageConfig(ctx context.Context, state *SettingsResourceModel, client *api.ClientWithResponses) diag.Diagnostics {
646+
// Use ProjectRef if Id is not set (during Create), otherwise use Id (during Read/Import)
647+
projectRef := state.Id.ValueString()
648+
if projectRef == "" {
649+
projectRef = state.ProjectRef.ValueString()
650+
}
651+
652+
httpResp, err := client.V1GetStorageConfigWithResponse(ctx, projectRef)
653+
if err != nil {
654+
msg := fmt.Sprintf("Unable to read storage settings, got error: %s", err)
655+
return diag.Diagnostics{diag.NewErrorDiagnostic("Client Error", msg)}
656+
}
657+
// Deleted project is an orphan resource, not returning error so it can be destroyed.
658+
switch httpResp.StatusCode() {
659+
case http.StatusNotFound, http.StatusNotAcceptable:
660+
return nil
661+
}
662+
if httpResp.JSON200 == nil {
663+
msg := fmt.Sprintf("Unable to read storage settings, got status %d: %s", httpResp.StatusCode(), httpResp.Body)
664+
return diag.Diagnostics{diag.NewErrorDiagnostic("Client Error", msg)}
665+
}
666+
667+
if state.Storage, err = parseConfig(state.Storage, *httpResp.JSON200); err != nil {
668+
msg := fmt.Sprintf("Unable to read storage settings, got error: %s", err)
669+
return diag.Diagnostics{diag.NewErrorDiagnostic("Client Error", msg)}
670+
}
671+
return nil
672+
}
673+
674+
func updateStorageConfig(ctx context.Context, plan *SettingsResourceModel, client *api.ClientWithResponses) diag.Diagnostics {
675+
var body api.UpdateStorageConfigBody
676+
if diags := plan.Storage.Unmarshal(&body); diags.HasError() {
677+
return diags
678+
}
679+
680+
httpResp, err := client.V1UpdateStorageConfigWithResponse(ctx, plan.ProjectRef.ValueString(), body)
681+
if err != nil {
682+
msg := fmt.Sprintf("Unable to update storage settings, got error: %s", err)
683+
return diag.Diagnostics{diag.NewErrorDiagnostic("Client Error", msg)}
684+
}
685+
if httpResp.StatusCode() != http.StatusOK {
686+
msg := fmt.Sprintf("Unable to update storage settings, got status %d: %s", httpResp.StatusCode(), httpResp.Body)
687+
return diag.Diagnostics{diag.NewErrorDiagnostic("Client Error", msg)}
688+
}
689+
690+
// Read back the updated config to get the actual state with correct field names
691+
return readStorageConfig(ctx, plan, client)
692+
}

0 commit comments

Comments
 (0)