Skip to content

Non-empty PostApplyPreRefresh plan of optional & computed ListNestedObject attribute with Required NestedAttribute #1101

@kenny-statsig

Description

@kenny-statsig

Module version

v1.14.1

Relevant provider source code

			"monitoring_metrics": schema.ListNestedAttribute{
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"name": schema.StringAttribute{
							Required: true,
						},
						"type": schema.StringAttribute{
							Required: true,
						},
					},
					CustomType: MonitoringMetricsType{
						ObjectType: types.ObjectType{
							AttrTypes: MonitoringMetricsValue{}.AttributeTypes(ctx),
						},
					},
				},
				Optional: true,
				Computed: true,
			},

Terraform Configuration Files

resource "statsig_gate" "my_gate" {
  name        = "my_gate"
  description = "A short description of what this Gate is used for."
  is_enabled  = true
  id_type     = "userID"
  rules = [
    {
      name            = "All Conditions"
      pass_percentage = 10
      environments    = ["production"]
      conditions = [
        {
          type         = "public"
          target_value = []
        },
        {
          type = "user_id"
          target_value = [
            "1", "2"
          ]
          operator = "any"
        },
        {
          type         = "email"
          target_value = ["@outlook.com", "@gmail.com"]
          operator     = "str_contains_any"
        },
        {
          type         = "custom_field"
          target_value = [31]
          operator     = "gt"
          field        = "age"
        },
        {
          type         = "app_version"
          target_value = ["1.1.1"]
          operator     = "version_gt"
        },
        {
          type         = "browser_name"
          target_value = ["Firefox", "Chrome"]
          operator     = "any"
        },
        {
          type         = "browser_version"
          target_value = ["94.0.4606.81", "94.0.4606.92"]
          operator     = "any"
        },
        {
          type         = "os_name"
          target_value = ["Android", "Windows"]
          operator     = "none"
        },
        {
          type         = "os_version"
          target_value = ["11.0.0"]
          operator     = "version_lte"
        },
        {
          type         = "country"
          target_value = ["NZ", "US"]
          operator     = "any"
        },
        {
          type         = "passes_gate"
          target_value = ["my_gate_2"]
        },
        {
          type         = "fails_gate"
          target_value = ["a_failing_gate"]
        },
        {
          type         = "time"
          target_value = [1643070357193]
          operator     = "after"
        },
        {
          type         = "environment_tier"
          target_value = ["production"]
          operator     = "any"
        },
        {
          type         = "passes_segment"
          target_value = ["growth_org"]
        },
        {
          type         = "fails_segment"
          target_value = ["promo_id_list"]
        },
        {
          type         = "ip_address"
          target_value = ["1.1.1.1", "8.8.8.8"]
          operator     = "any"
        }
      ]
    },
    {
      name            = "Development Conditions"
      pass_percentage = 10
      conditions = [
        {
          type         = "public"
          target_value = []
        }
      ]
      environments = ["development"]
    }
  ]
}

output "my_gate" {
  value = statsig_gate.my_gate
}

Debug Output

https://gist.github.com/kenny-statsig/520dfc886304df384b44d816bfa179ff

Expected Behavior

The provider should have an empty non-refresh plan

Actual Behavior

The acceptance test fails due to a non-empty plan. The prior state doesn't match the planned state because the planned state is null for monitoring_metrics even though the state is being set properly upon Create (output of resp.State.Raw https://gist.github.com/kenny-statsig/83a940eed74ef791d56ef824bf0076f2)
Prior: "monitoring_metrics":tftypes.List[tftypes.Object["name":tftypes.String, "type":tftypes.String]]<tftypes.Object["name":tftypes.String, "type":tftypes.String]<"name":tftypes.String<"dau">, "type":tftypes.String<"user">>>
Planned: "monitoring_metrics":tftypes.List[tftypes.Object["name":tftypes.String, "type":tftypes.String]]<null>
Full diff: https://www.diffchecker.com/YT9HvE9p/

Here are some code snippets of the provider's implementation
gate.go https://gist.github.com/kenny-statsig/d5a3f43021df0b0d5fd30216103ee70b
util.go https://gist.github.com/kenny-statsig/dad3239d769dc967a44ef061b0d566dd
gate_resource.go https://gist.github.com/kenny-statsig/b3b9843e27fed30f0c90b018e428e4e1

Steps to Reproduce

Provider is still in development so cannot be reproduced externally.
But it's a simple acceptance test

func TestAccGateFull_MUX(t *testing.T) {
	resource.Test(t, resource.TestCase{
		ProtoV6ProviderFactories: protoV6ProviderFactories(),
		PreCheck:                 func() { testAccPreCheck(t) },
		Steps: []resource.TestStep{
			{
				ConfigFile: config.StaticFile("test_resources/gate_full.tf"),
				Check: resource.ComposeTestCheckFunc(
					verifyFullGateSetup(t, "statsig_gate.my_gate"),
					verifyFullGateOutput(t),
				),
			},
			{
				ConfigFile: config.StaticFile("test_resources/gate_full.tf"),
				ConfigPlanChecks: resource.ConfigPlanChecks{
					PreApply: []plancheck.PlanCheck{
						plancheck.ExpectEmptyPlan(),
					},
				},
			},
		},
	})
}

References

Seems to be the same issue described here https://discuss.hashicorp.com/t/computed-attributes-and-plan-modifiers/45830/20

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions