Skip to content

Commit

Permalink
Merge pull request #702 from okta/schema_properties
Browse files Browse the repository at this point in the history
Fixed setup of array elements for schema properties
  • Loading branch information
monde authored Oct 11, 2021
2 parents 9bbad86 + 799a173 commit 521245b
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 108 deletions.
22 changes: 22 additions & 0 deletions examples/okta_user_schema_property/array_number.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
resource "okta_user_schema_property" "test" {
index = "testAcc_replace_with_uuid"
title = "terraform acceptance test"
type = "array"
description = "testing"
master = "OKTA"
scope = "SELF"
array_type = "number"
array_enum = [0.01, 0.02, 0.03]
array_one_of {
title = "1"
const = 0.01
}
array_one_of {
title = "2"
const = 0.02
}
array_one_of {
title = "3"
const = 0.03
}
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ require (
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/okta/okta-sdk-golang/v2 v2.6.3-0.20210923165359-20aeac44ab01
github.com/okta/okta-sdk-golang/v2 v2.8.1-0.20211009204024-cb628b5d2137
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
github.com/russellhaering/goxmldsig v1.1.0 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/zclconf/go-cty v1.8.4 // indirect
go.opencensus.io v0.22.4 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,10 @@ github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/okta/okta-sdk-golang/v2 v2.6.3-0.20210923165359-20aeac44ab01 h1:eTw4NCD4FBAAT+tqx8qq2WhPjVwauVp//mvG4c3wrUY=
github.com/okta/okta-sdk-golang/v2 v2.6.3-0.20210923165359-20aeac44ab01/go.mod h1:0y8stgdplWMjaEbMr4mVtw0R+BdktpGZRw2sWKZWsMs=
github.com/okta/okta-sdk-golang/v2 v2.8.1-0.20211009194114-60ca24b20d8c h1:Lpcx6URUxikIRO7seg6me0H8nRXzN4ZhF+XUt1t2+WA=
github.com/okta/okta-sdk-golang/v2 v2.8.1-0.20211009194114-60ca24b20d8c/go.mod h1:0y8stgdplWMjaEbMr4mVtw0R+BdktpGZRw2sWKZWsMs=
github.com/okta/okta-sdk-golang/v2 v2.8.1-0.20211009204024-cb628b5d2137 h1:Ku0kX7LW6isMNVdr/V1myw0SFoAQoW+yKXQh3HG/t54=
github.com/okta/okta-sdk-golang/v2 v2.8.1-0.20211009204024-cb628b5d2137/go.mod h1:0y8stgdplWMjaEbMr4mVtw0R+BdktpGZRw2sWKZWsMs=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -365,8 +367,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down
10 changes: 8 additions & 2 deletions okta/resource_okta_app_user_custom_schema_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ func setAppUserSchemaProperty(ctx context.Context, d *schema.ResourceData, m int
bOff.InitialInterval = time.Second
err = backoff.Retry(func() error {
if err := updateAppUserSubSchemaProperty(ctx, d, m); err != nil {
if errors.Is(err, errInvalidElemFormat) {
return backoff.Permanent(err)
}
return err
}
us, resp, err := getOktaClientFromMetadata(m).UserSchema.GetApplicationUserSchema(ctx, d.Get("app_id").(string))
Expand Down Expand Up @@ -184,7 +187,10 @@ func resourceAppUserSchemaPropertyDelete(ctx context.Context, d *schema.Resource
}

func updateAppUserSubSchemaProperty(ctx context.Context, d *schema.ResourceData, m interface{}) error {
subSchema := buildUserCustomSchemaAttribute(d)
subSchema, err := buildUserCustomSchemaAttribute(d)
if err != nil {
return err
}
if d.Get("union").(bool) {
subSchema.Union = "ENABLE"
} else {
Expand All @@ -194,7 +200,7 @@ func updateAppUserSubSchemaProperty(ctx context.Context, d *schema.ResourceData,
bOff := backoff.NewExponentialBackOff()
bOff.MaxElapsedTime = time.Second * 10
bOff.InitialInterval = time.Second
err := backoff.Retry(func() error {
err = backoff.Retry(func() error {
_, _, err := getOktaClientFromMetadata(m).UserSchema.
UpdateApplicationUserProfile(ctx, d.Get("app_id").(string), *custom)
if err == nil {
Expand Down
100 changes: 61 additions & 39 deletions okta/resource_okta_group_custom_schema_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"
"time"

"github.com/cenkalti/backoff/v4"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/okta-sdk-golang/v2/okta"
Expand Down Expand Up @@ -72,7 +74,11 @@ func resourceGroupSchemaCreateOrUpdate(ctx context.Context, d *schema.ResourceDa
if err != nil {
return diag.FromErr(err)
}
custom := buildCustomGroupSchema(d.Get("index").(string), buildGroupCustomSchemaAttribute(d))
groupCustomSchemaAttribute, err := buildGroupCustomSchemaAttribute(d)
if err != nil {
return diag.FromErr(err)
}
custom := buildCustomGroupSchema(d.Get("index").(string), groupCustomSchemaAttribute)
subSchema, err := alterCustomGroupSchema(ctx, m, d.Get("index").(string), custom, false)
if err != nil {
return diag.Errorf("failed to create or update group custom schema property %s: %v", d.Get("index").(string), err)
Expand Down Expand Up @@ -105,40 +111,38 @@ func resourceGroupSchemaRead(ctx context.Context, d *schema.ResourceData, m inte

func alterCustomGroupSchema(ctx context.Context, m interface{}, index string, schema *okta.GroupSchema, isDeleteOperation bool) (*okta.GroupSchemaAttribute, error) {
var schemaAttribute *okta.GroupSchemaAttribute
timer := time.NewTimer(time.Second * 120) // sometimes it takes some time (several attempts) to recreate/delete group schema property
ticker := time.NewTicker(time.Second * 5)
loop:
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-timer.C:
return nil, errors.New("no more attempts left")
case <-ticker.C:
updated, resp, err := getOktaClientFromMetadata(m).GroupSchema.UpdateGroupSchema(ctx, *schema)
if err != nil {
if resp != nil && resp.StatusCode == 500 {
logger(m).Debug("updating group custom schema property caused 500 error", err)
continue
}
if strings.Contains(err.Error(), "Wait until the data clean up process finishes and then try again") {
continue
}
return nil, err
}
s, _, err := getOktaClientFromMetadata(m).GroupSchema.GetGroupSchema(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get group custom schema property: %v", err)

bOff := backoff.NewExponentialBackOff()
bOff.MaxElapsedTime = time.Second * 120
bOff.InitialInterval = time.Second
bc := backoff.WithContext(bOff, ctx)

err := backoff.Retry(func() error {
updated, resp, err := getOktaClientFromMetadata(m).GroupSchema.UpdateGroupSchema(ctx, *schema)
if err != nil {
logger(m).Error(err.Error())
if resp != nil && resp.StatusCode == 500 {
return fmt.Errorf("updating group custom schema property caused 500 error: %w", err)
}
schemaAttribute = groupSchemaCustomAttribute(s, index)
if isDeleteOperation && schemaAttribute == nil {
break loop
} else if schemaAttribute != nil && reflect.DeepEqual(schemaAttribute, updated.Definitions.Custom.Properties[index]) {
break loop
if strings.Contains(err.Error(), "Wait until the data clean up process finishes and then try again") {
return err
}
return backoff.Permanent(err)
}
}
return schemaAttribute, nil
s, _, err := getOktaClientFromMetadata(m).GroupSchema.GetGroupSchema(ctx)
if err != nil {
return backoff.Permanent(fmt.Errorf("failed to get group custom schema property: %v", err))
}
schemaAttribute = groupSchemaCustomAttribute(s, index)
if isDeleteOperation && schemaAttribute == nil {
return nil
} else if schemaAttribute != nil && reflect.DeepEqual(schemaAttribute, updated.Definitions.Custom.Properties[index]) {
return nil
}
logger(m).Error("failed to apply changes after several retries")
return errors.New("failed to apply changes after several retries")
}, bc)
return schemaAttribute, err
}

func resourceGroupSchemaDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
Expand Down Expand Up @@ -176,10 +180,10 @@ func syncCustomGroupSchema(d *schema.ResourceData, subschema *okta.GroupSchemaAt
if subschema.Items != nil {
_ = d.Set("array_type", subschema.Items.Type)
_ = d.Set("array_one_of", flattenOneOf(subschema.Items.OneOf))
_ = d.Set("array_enum", convertStringSliceToInterfaceSlice(subschema.Items.Enum))
_ = d.Set("array_enum", flattenEnum(subschema.Items.Enum))
}
if len(subschema.Enum) > 0 {
_ = d.Set("enum", convertStringSliceToInterfaceSlice(subschema.Enum))
_ = d.Set("enum", flattenEnum(subschema.Enum))
}
return setNonPrimitives(d, map[string]interface{}{
"one_of": flattenOneOf(subschema.OneOf),
Expand Down Expand Up @@ -208,7 +212,25 @@ func syncBaseGroupSchema(d *schema.ResourceData, subschema *okta.GroupSchemaAttr
}
}

func buildGroupCustomSchemaAttribute(d *schema.ResourceData) *okta.GroupSchemaAttribute {
func buildGroupCustomSchemaAttribute(d *schema.ResourceData) (*okta.GroupSchemaAttribute, error) {
items, err := buildNullableItems(d)
if err != nil {
return nil, err
}
var oneOf []*okta.UserSchemaAttributeEnum
if rawOneOf, ok := d.GetOk("one_of"); ok {
oneOf, err = buildOneOf(rawOneOf.([]interface{}), d.Get("type").(string))
if err != nil {
return nil, err
}
}
var enum []interface{}
if rawEnum, ok := d.GetOk("enum"); ok {
enum, err = buildEnum(rawEnum.([]interface{}), d.Get("type").(string))
if err != nil {
return nil, err
}
}
return &okta.GroupSchemaAttribute{
Title: d.Get("title").(string),
Type: d.Get("type").(string),
Expand All @@ -221,16 +243,16 @@ func buildGroupCustomSchemaAttribute(d *schema.ResourceData) *okta.GroupSchemaAt
},
},
Scope: d.Get("scope").(string),
Enum: convertInterfaceToStringArrNullable(d.Get("enum")),
Enum: enum,
Master: getNullableMaster(d),
Items: getNullableItem(d),
Items: items,
MinLength: int64(d.Get("min_length").(int)),
MaxLength: int64(d.Get("max_length").(int)),
OneOf: getNullableOneOf(d, "one_of"),
OneOf: oneOf,
ExternalName: d.Get("external_name").(string),
ExternalNamespace: d.Get("external_namespace").(string),
Unique: d.Get("unique").(string),
}
}, nil
}

func groupSchemaCustomAttribute(s *okta.GroupSchema, index string) *okta.GroupSchemaAttribute {
Expand Down
67 changes: 35 additions & 32 deletions okta/resource_okta_user_custom_schema_property.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/okta-sdk-golang/v2/okta"
Expand Down Expand Up @@ -108,7 +109,11 @@ func resourceUserSchemaCreateOrUpdate(ctx context.Context, d *schema.ResourceDat
if err != nil {
return diag.FromErr(err)
}
custom := buildCustomUserSchema(d.Get("index").(string), buildUserCustomSchemaAttribute(d))
userCustomSchemaAttribute, err := buildUserCustomSchemaAttribute(d)
if err != nil {
return diag.FromErr(err)
}
custom := buildCustomUserSchema(d.Get("index").(string), userCustomSchemaAttribute)
subSchema, err := alterCustomUserSchema(ctx, m, d.Get("user_type").(string), d.Get("index").(string), custom, false)
if err != nil {
return diag.Errorf("failed to create or update user custom schema property %s: %v", d.Get("index").(string), err)
Expand Down Expand Up @@ -149,40 +154,38 @@ func alterCustomUserSchema(ctx context.Context, m interface{}, userType, index s
return nil, err
}
var schemaAttribute *okta.UserSchemaAttribute
timer := time.NewTimer(time.Second * 60) // sometimes it takes some time (several attempts) to recreate/delete user schema property
ticker := time.NewTicker(time.Second * 5)
loop:
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-timer.C:
return nil, errors.New("no more attempts left")
case <-ticker.C:
updated, resp, err := getOktaClientFromMetadata(m).UserSchema.UpdateUserProfile(ctx, typeSchemaID, *schema)
if err != nil {
if resp != nil && resp.StatusCode == 500 {
logger(m).Debug("updating user custom schema property caused 500 error", err)
continue
}
if strings.Contains(err.Error(), "Wait until the data clean up process finishes and then try again") {
continue
}
return nil, err
}
s, _, err := getOktaClientFromMetadata(m).UserSchema.GetUserSchema(ctx, typeSchemaID)
if err != nil {
return nil, fmt.Errorf("failed to get user custom schema property: %v", err)

bOff := backoff.NewExponentialBackOff()
bOff.MaxElapsedTime = time.Second * 120
bOff.InitialInterval = time.Second
bc := backoff.WithContext(bOff, ctx)

err = backoff.Retry(func() error {
updated, resp, err := getOktaClientFromMetadata(m).UserSchema.UpdateUserProfile(ctx, typeSchemaID, *schema)
if err != nil {
logger(m).Error(err.Error())
if resp != nil && resp.StatusCode == 500 {
return fmt.Errorf("updating user custom schema property caused 500 error: %w", err)
}
schemaAttribute = userSchemaCustomAttribute(s, index)
if isDeleteOperation && schemaAttribute == nil {
break loop
} else if schemaAttribute != nil && reflect.DeepEqual(schemaAttribute, updated.Definitions.Custom.Properties[index]) {
break loop
if strings.Contains(err.Error(), "Wait until the data clean up process finishes and then try again") {
return err
}
return backoff.Permanent(err)
}
}
return schemaAttribute, nil
s, _, err := getOktaClientFromMetadata(m).UserSchema.GetUserSchema(ctx, typeSchemaID)
if err != nil {
return backoff.Permanent(fmt.Errorf("failed to get user custom schema property: %v", err))
}
schemaAttribute = userSchemaCustomAttribute(s, index)
if isDeleteOperation && schemaAttribute == nil {
return nil
} else if schemaAttribute != nil && reflect.DeepEqual(schemaAttribute, updated.Definitions.Custom.Properties[index]) {
return nil
}
logger(m).Error("failed to apply changes after several retries")
return errors.New("failed to apply changes after several retries")
}, bc)
return schemaAttribute, err
}

func resourceUserSchemaDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
Expand Down
19 changes: 19 additions & 0 deletions okta/resource_okta_user_custom_schema_property_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func TestAccOktaUserSchema_arrayString(t *testing.T) {
config := mgr.GetFixtures("array_string.tf", ri, t)
updatedConfig := mgr.GetFixtures("array_string_updated.tf", ri, t)
arrayEnum := mgr.GetFixtures("array_enum.tf", ri, t)
arrayNumber := mgr.GetFixtures("array_number.tf", ri, t)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -194,6 +195,24 @@ func TestAccOktaUserSchema_arrayString(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "array_one_of.#", "3"),
),
},
{
Config: arrayNumber,
Check: resource.ComposeTestCheckFunc(
testOktaUserSchemasExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "index", "testAcc_"+strconv.Itoa(ri)),
resource.TestCheckResourceAttr(resourceName, "title", "terraform acceptance test"),
resource.TestCheckResourceAttr(resourceName, "type", "array"),
resource.TestCheckResourceAttr(resourceName, "array_type", "number"),
resource.TestCheckResourceAttr(resourceName, "description", "testing"),
resource.TestCheckResourceAttr(resourceName, "required", "false"),
resource.TestCheckResourceAttr(resourceName, "master", "OKTA"),
resource.TestCheckResourceAttr(resourceName, "scope", "SELF"),
resource.TestCheckResourceAttr(resourceName, "array_enum.0", "0.01"),
resource.TestCheckResourceAttr(resourceName, "array_enum.1", "0.02"),
resource.TestCheckResourceAttr(resourceName, "array_enum.2", "0.03"),
resource.TestCheckResourceAttr(resourceName, "array_one_of.#", "3"),
),
},
{
ResourceName: resourceName,
ImportState: true,
Expand Down
Loading

0 comments on commit 521245b

Please sign in to comment.