diff --git a/.changes/unreleased/Fixed-20241217-190228.yaml b/.changes/unreleased/Fixed-20241217-190228.yaml new file mode 100644 index 00000000..f191069c --- /dev/null +++ b/.changes/unreleased/Fixed-20241217-190228.yaml @@ -0,0 +1,3 @@ +kind: Fixed +body: This adds a sorting of the permissions we get from the commercetools API response, so that it's identical to the one from the plan. +time: 2024-12-17T19:02:28.854328+01:00 diff --git a/internal/resources/associate_role/resource.go b/internal/resources/associate_role/resource.go index 6d7029df..4def28a8 100644 --- a/internal/resources/associate_role/resource.go +++ b/internal/resources/associate_role/resource.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "regexp" + "sort" "time" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" @@ -138,6 +139,35 @@ func (*associateRoleResource) Metadata(_ context.Context, req resource.MetadataR resp.TypeName = req.ProviderTypeName + "_associate_role" } +func resortPermissions(permissions, plan []platform.Permission) []platform.Permission { + + // Build a map which maps the planned permissions to its index from the array + indexMap := make(map[platform.Permission]int) + for idx, p := range plan { + indexMap[p] = idx + } + + // Build a map of the current permissions to the planned index + targetMap := make(map[platform.Permission]int) + for _, p := range permissions { + idx, ok := indexMap[p] + if ok { + targetMap[p] = idx + } + } + + // Sort the target permission list by the index from the map + targetList := make([]platform.Permission, 0, len(targetMap)) + for key := range targetMap { + targetList = append(targetList, key) + } + sort.SliceStable(targetList, func(i, j int) bool{ + return targetMap[targetList[i]] < targetMap[targetList[j]] + }) + + return targetList +} + // Create implements resource.Resource. func (r *associateRoleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var plan AssociateRole @@ -163,6 +193,8 @@ func (r *associateRoleResource) Create(ctx context.Context, req resource.CreateR return } + associateRole.Permissions = resortPermissions(associateRole.Permissions, draft.Permissions); + current := NewAssociateRoleFromNative(associateRole) diags = resp.State.Set(ctx, current) @@ -228,6 +260,8 @@ func (r *associateRoleResource) Read(ctx context.Context, req resource.ReadReque return } + associateRole.Permissions = resortPermissions(associateRole.Permissions, state.draft().Permissions); + // Transform the remote platform associate role to the // tf schema matching representation. current := NewAssociateRoleFromNative(associateRole) @@ -275,6 +309,8 @@ func (r *associateRoleResource) Update(ctx context.Context, req resource.UpdateR return } + associateRole.Permissions = resortPermissions(associateRole.Permissions, plan.draft().Permissions) + current := NewAssociateRoleFromNative(associateRole) diags = resp.State.Set(ctx, current) @@ -297,4 +333,4 @@ func (r *associateRoleResource) Configure(_ context.Context, req resource.Config // ImportState implements resource.ResourceWithImportState. func (*associateRoleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} +} \ No newline at end of file