Skip to content

Commit

Permalink
r/aws_cognitoidp_user_pool: suppress diff when schema.string_attribut…
Browse files Browse the repository at this point in the history
…e_constraints is omitted
  • Loading branch information
jar-b committed Jul 10, 2023
1 parent 9025260 commit 8b54529
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 1 deletion.
101 changes: 101 additions & 0 deletions internal/service/cognitoidp/flex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,104 @@ func TestUserPoolSchemaAttributeMatchesStandardAttribute(t *testing.T) {
}
}
}

func TestSkipFlatteningStringAttributeContraints(t *testing.T) {
t.Parallel()

cases := []struct {
name string
configured []*cognitoidentityprovider.SchemaAttributeType
input *cognitoidentityprovider.SchemaAttributeType
want bool
}{
{
name: "config omitted",
configured: []*cognitoidentityprovider.SchemaAttributeType{
{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
},
},
input: &cognitoidentityprovider.SchemaAttributeType{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
StringAttributeConstraints: &cognitoidentityprovider.StringAttributeConstraintsType{
MaxLength: aws.String("2048"),
MinLength: aws.String("0"),
},
},
want: true,
},
{
name: "config set",
configured: []*cognitoidentityprovider.SchemaAttributeType{
{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
StringAttributeConstraints: &cognitoidentityprovider.StringAttributeConstraintsType{
MaxLength: aws.String("2048"),
MinLength: aws.String("0"),
},
},
},
input: &cognitoidentityprovider.SchemaAttributeType{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
StringAttributeConstraints: &cognitoidentityprovider.StringAttributeConstraintsType{
MaxLength: aws.String("2048"),
MinLength: aws.String("0"),
},
},
want: false,
},
{
name: "config set with diff",
configured: []*cognitoidentityprovider.SchemaAttributeType{
{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
StringAttributeConstraints: &cognitoidentityprovider.StringAttributeConstraintsType{
MaxLength: aws.String("1024"),
MinLength: aws.String("5"),
},
},
},
input: &cognitoidentityprovider.SchemaAttributeType{
AttributeDataType: aws.String(cognitoidentityprovider.AttributeDataTypeString),
DeveloperOnlyAttribute: aws.Bool(false),
Mutable: aws.Bool(false),
Name: aws.String("email"),
Required: aws.Bool(true),
StringAttributeConstraints: &cognitoidentityprovider.StringAttributeConstraintsType{
MaxLength: aws.String("2048"),
MinLength: aws.String("0"),
},
},
want: false,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := skipFlatteningStringAttributeContraints(tc.configured, tc.input)
if got != tc.want {
t.Fatalf("skipFlatteningStringAttributeContraints() got %t, want %t\n\n%#v\n\n", got, tc.want, tc.input)
}
})
}
}
25 changes: 24 additions & 1 deletion internal/service/cognitoidp/user_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1813,7 +1813,7 @@ func flattenUserPoolSchema(configuredAttributes, inputs []*cognitoidentityprovid
value["number_attribute_constraints"] = []map[string]interface{}{subvalue}
}

if input.StringAttributeConstraints != nil {
if input.StringAttributeConstraints != nil && !skipFlatteningStringAttributeContraints(configuredAttributes, input) {
subvalue := make(map[string]interface{})

if input.StringAttributeConstraints.MinLength != nil {
Expand Down Expand Up @@ -2300,3 +2300,26 @@ func flattenUserPoolUserAttributeUpdateSettings(u *cognitoidentityprovider.UserA

return []map[string]interface{}{m}
}

// skipFlatteningStringAttributeContraints returns true when all of the schema arguments
// match an existing configured attribute, except an empty "string_attribute_constraints" block.
// In this situation the Describe API returns default constraint values, and a persistent diff
// would be present if written to state.
func skipFlatteningStringAttributeContraints(configuredAttributes []*cognitoidentityprovider.SchemaAttributeType, input *cognitoidentityprovider.SchemaAttributeType) bool {
skip := false
for _, configuredAttribute := range configuredAttributes {
// Root elements are all equal
if reflect.DeepEqual(input.AttributeDataType, configuredAttribute.AttributeDataType) &&
reflect.DeepEqual(input.DeveloperOnlyAttribute, configuredAttribute.DeveloperOnlyAttribute) &&
reflect.DeepEqual(input.Mutable, configuredAttribute.Mutable) &&
reflect.DeepEqual(input.Name, configuredAttribute.Name) &&
reflect.DeepEqual(input.Required, configuredAttribute.Required) &&
// The configured "string_attribute_constraints" object is empty, but the returned value is not
(aws.StringValue(configuredAttribute.AttributeDataType) == cognitoidentityprovider.AttributeDataTypeString &&
configuredAttribute.StringAttributeConstraints == nil &&
input.StringAttributeConstraints != nil) {
skip = true
}
}
return skip
}
55 changes: 55 additions & 0 deletions internal/service/cognitoidp/user_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,37 @@ func TestAccCognitoIDPUserPool_schemaAttributesModified(t *testing.T) {
})
}

// Ref: https://github.com/hashicorp/terraform-provider-aws/issues/21654
func TestAccCognitoIDPUserPool_schemaAttributesStringAttributeConstraints(t *testing.T) {
ctx := acctest.Context(t)
var pool cognitoidentityprovider.DescribeUserPoolOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_cognito_user_pool.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckIdentityProvider(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, cognitoidentityprovider.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserPoolDestroy(ctx),
Steps: []resource.TestStep{
{
// Omit optional "string_attribute_constraints" schema argument to verify a persistent
// diff is not present when AWS returns default values in the nested object.
Config: testAccUserPoolConfig_schemaAttributesStringAttributeConstraints(rName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckUserPoolExists(ctx, resourceName, &pool),
),
},
{
// Attempting to explicitly set constraints to non-default values after creation
// should trigger an error
Config: testAccUserPoolConfig_schemaAttributes(rName),
ExpectError: regexp.MustCompile("cannot modify or remove schema items"),
},
},
})
}

func TestAccCognitoIDPUserPool_withVerificationMessageTemplate(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
Expand Down Expand Up @@ -2458,6 +2489,30 @@ resource "aws_cognito_user_pool" "test" {
`, name, boolname)
}

func testAccUserPoolConfig_schemaAttributesStringAttributeConstraints(name string) string {
return fmt.Sprintf(`
resource "aws_cognito_user_pool" "test" {
name = "%[1]s"
schema {
attribute_data_type = "String"
developer_only_attribute = false
mutable = false
name = "email"
required = true
}
schema {
attribute_data_type = "Boolean"
developer_only_attribute = true
mutable = false
name = "mybool"
required = false
}
}
`, name)
}

func testAccUserPoolConfig_verificationMessageTemplate(name string) string {
return fmt.Sprintf(`
resource "aws_cognito_user_pool" "test" {
Expand Down

0 comments on commit 8b54529

Please sign in to comment.