Skip to content

Commit a2c25be

Browse files
authored
fix(mongodb): User role should be updatable (#731)
Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>
1 parent 91903f5 commit a2c25be

2 files changed

Lines changed: 168 additions & 3 deletions

File tree

stackit/internal/services/mongodbflex/user/resource.go

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,72 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp
287287
}
288288

289289
// Update updates the resource and sets the updated Terraform state on success.
290-
func (r *userResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
291-
// Update shouldn't be called
292-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", "User can't be updated")
290+
func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
291+
// Retrieve values from plan
292+
var model Model
293+
diags := req.Plan.Get(ctx, &model)
294+
resp.Diagnostics.Append(diags...)
295+
if resp.Diagnostics.HasError() {
296+
return
297+
}
298+
projectId := model.ProjectId.ValueString()
299+
instanceId := model.InstanceId.ValueString()
300+
userId := model.UserId.ValueString()
301+
ctx = tflog.SetField(ctx, "project_id", projectId)
302+
ctx = tflog.SetField(ctx, "instance_id", instanceId)
303+
ctx = tflog.SetField(ctx, "user_id", userId)
304+
305+
// Retrieve values from state
306+
var stateModel Model
307+
diags = req.State.Get(ctx, &stateModel)
308+
resp.Diagnostics.Append(diags...)
309+
if resp.Diagnostics.HasError() {
310+
return
311+
}
312+
313+
var roles []string
314+
if !(model.Roles.IsNull() || model.Roles.IsUnknown()) {
315+
diags = model.Roles.ElementsAs(ctx, &roles, false)
316+
resp.Diagnostics.Append(diags...)
317+
if resp.Diagnostics.HasError() {
318+
return
319+
}
320+
}
321+
322+
// Generate API request body from model
323+
payload, err := toUpdatePayload(&model, roles)
324+
if err != nil {
325+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Updating API payload: %v", err))
326+
return
327+
}
328+
329+
// Update existing instance
330+
err = r.client.UpdateUser(ctx, projectId, instanceId, userId).UpdateUserPayload(*payload).Execute()
331+
if err != nil {
332+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", err.Error())
333+
return
334+
}
335+
336+
userResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
337+
if err != nil {
338+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Calling API: %v", err))
339+
return
340+
}
341+
342+
// Map response body to schema
343+
err = mapFields(userResp, &stateModel)
344+
if err != nil {
345+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Processing API payload: %v", err))
346+
return
347+
}
348+
349+
// Set state to fully populated data
350+
diags = resp.State.Set(ctx, stateModel)
351+
resp.Diagnostics.Append(diags...)
352+
if resp.Diagnostics.HasError() {
353+
return
354+
}
355+
tflog.Info(ctx, "MongoDB Flex user updated")
293356
}
294357

295358
// Delete deletes the resource and removes the Terraform state on success.
@@ -450,3 +513,17 @@ func toCreatePayload(model *Model, roles []string) (*mongodbflex.CreateUserPaylo
450513
Database: conversion.StringValueToPointer(model.Database),
451514
}, nil
452515
}
516+
517+
func toUpdatePayload(model *Model, roles []string) (*mongodbflex.UpdateUserPayload, error) {
518+
if model == nil {
519+
return nil, fmt.Errorf("nil model")
520+
}
521+
if roles == nil {
522+
return nil, fmt.Errorf("nil roles")
523+
}
524+
525+
return &mongodbflex.UpdateUserPayload{
526+
Roles: &roles,
527+
Database: conversion.StringValueToPointer(model.Database),
528+
}, nil
529+
}

stackit/internal/services/mongodbflex/user/resource_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,91 @@ func TestToCreatePayload(t *testing.T) {
377377
})
378378
}
379379
}
380+
381+
func TestToUpdatePayload(t *testing.T) {
382+
tests := []struct {
383+
description string
384+
input *Model
385+
inputRoles []string
386+
expected *mongodbflex.UpdateUserPayload
387+
isValid bool
388+
}{
389+
{
390+
"default_values",
391+
&Model{},
392+
[]string{},
393+
&mongodbflex.UpdateUserPayload{
394+
Roles: &[]string{},
395+
Database: nil,
396+
},
397+
true,
398+
},
399+
{
400+
"simple values",
401+
&Model{
402+
Username: types.StringValue("username"),
403+
Database: types.StringValue("database"),
404+
},
405+
[]string{
406+
"role_1",
407+
"role_2",
408+
},
409+
&mongodbflex.UpdateUserPayload{
410+
Roles: &[]string{
411+
"role_1",
412+
"role_2",
413+
},
414+
Database: utils.Ptr("database"),
415+
},
416+
true,
417+
},
418+
{
419+
"null_fields",
420+
&Model{
421+
Username: types.StringNull(),
422+
Database: types.StringNull(),
423+
},
424+
[]string{
425+
"",
426+
},
427+
&mongodbflex.UpdateUserPayload{
428+
Roles: &[]string{
429+
"",
430+
},
431+
Database: nil,
432+
},
433+
true,
434+
},
435+
{
436+
"nil_model",
437+
nil,
438+
[]string{},
439+
nil,
440+
false,
441+
},
442+
{
443+
"nil_roles",
444+
&Model{},
445+
nil,
446+
nil,
447+
false,
448+
},
449+
}
450+
for _, tt := range tests {
451+
t.Run(tt.description, func(t *testing.T) {
452+
output, err := toUpdatePayload(tt.input, tt.inputRoles)
453+
if !tt.isValid && err == nil {
454+
t.Fatalf("Should have failed")
455+
}
456+
if tt.isValid && err != nil {
457+
t.Fatalf("Should not have failed: %v", err)
458+
}
459+
if tt.isValid {
460+
diff := cmp.Diff(output, tt.expected)
461+
if diff != "" {
462+
t.Fatalf("Data does not match: %s", diff)
463+
}
464+
}
465+
})
466+
}
467+
}

0 commit comments

Comments
 (0)