Skip to content

Commit

Permalink
fix update (#23263)
Browse files Browse the repository at this point in the history
  • Loading branch information
teowa authored Sep 19, 2023
1 parent 7112e27 commit 4c0ff69
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 82 deletions.
249 changes: 169 additions & 80 deletions internal/services/appconfiguration/app_configuration_feature_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-sdk/resource-manager/appconfiguration/2023-03-01/configurationstores"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
Expand Down Expand Up @@ -46,7 +47,7 @@ type FeatureResourceModel struct {
Label string `tfschema:"label"`
Locked bool `tfschema:"locked"`
Tags map[string]interface{} `tfschema:"tags"`
PercentageFilter int `tfschema:"percentage_filter_value"`
PercentageFilter float64 `tfschema:"percentage_filter_value"`
TimewindowFilters []TimewindowFilterParameters `tfschema:"timewindow_filter"`
TargetingFilters []TargetingFilterAudience `tfschema:"targeting_filter"`
}
Expand Down Expand Up @@ -96,9 +97,9 @@ func (k FeatureResource) Arguments() map[string]*pluginsdk.Schema {
Default: false,
},
"percentage_filter_value": {
Type: pluginsdk.TypeInt,
Type: pluginsdk.TypeFloat,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
ValidateFunc: validation.FloatBetween(0, 100),
},
"targeting_filter": {
Type: pluginsdk.TypeList,
Expand Down Expand Up @@ -242,9 +243,64 @@ func (k FeatureResource) Create() sdk.ResourceFunc {
return tf.ImportAsExistsError(k.ResourceType(), nestedItemId.ID())
}

err = createOrUpdateFeature(ctx, client, model)
entity := appconfiguration.KeyValue{
Key: pointer.To(featureKey),
Label: pointer.To(model.Label),
Tags: tags.Expand(model.Tags),
ContentType: pointer.To(FeatureKeyContentType),
Locked: pointer.To(model.Locked),
}

value := FeatureValue{
ID: model.Name,
Description: model.Description,
Enabled: model.Enabled,
}

value.Conditions.ClientFilters.Filters = make([]interface{}, 0)

if model.PercentageFilter > 0 {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, PercentageFeatureFilter{
Name: PercentageFilterName,
Parameters: PercentageFilterParameters{Value: model.PercentageFilter},
})
}

if len(model.TargetingFilters) > 0 {
for _, tgtf := range model.TargetingFilters {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, TargetingFeatureFilter{
Name: TargetingFilterName,
Parameters: TargetingFilterParameters{Audience: tgtf},
})
}
}

if len(model.TimewindowFilters) > 0 {
for _, twf := range model.TimewindowFilters {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, TimewindowFeatureFilter{
Name: TimewindowFilterName,
Parameters: twf,
})
}
}

valueBytes, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("while creating feature: %+v", err)
return fmt.Errorf("while marshalling FeatureValue struct: %+v", err)
}
entity.Value = pointer.To(string(valueBytes))
if _, err = client.PutKeyValue(ctx, featureKey, model.Label, &entity, "", ""); err != nil {
return err
}

if model.Locked {
if _, err = client.PutLock(ctx, featureKey, model.Label, "", ""); err != nil {
return fmt.Errorf("while locking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
} else {
if _, err = client.DeleteLock(ctx, featureKey, model.Label, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
}

// https://github.com/Azure/AppConfiguration/issues/763
Expand Down Expand Up @@ -373,6 +429,17 @@ func (k FeatureResource) Update() sdk.ResourceFunc {
return err
}

kv, err := client.GetKeyValue(ctx, nestedItemId.Key, nestedItemId.Label, "", "", "", []appconfiguration.KeyValueFields{})
if err != nil {
return fmt.Errorf("while checking for key %q existence: %+v", *nestedItemId, err)
}

var fv FeatureValue
err = json.Unmarshal([]byte(utils.NormalizeNilableString(kv.Value)), &fv)
if err != nil {
return fmt.Errorf("while unmarshalling underlying key's value: %+v", err)
}

var model FeatureResourceModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding %+v", err)
Expand All @@ -385,14 +452,104 @@ func (k FeatureResource) Update() sdk.ResourceFunc {

metadata.Client.AppConfiguration.AddToCache(*configurationStoreId, nestedItemId.ConfigurationStoreEndpoint)

if metadata.ResourceData.HasChange("tags") || metadata.ResourceData.HasChange("enabled") || metadata.ResourceData.HasChange("locked") || metadata.ResourceData.HasChange("description") {
// Remove the lock, if any. We will put it back again if the model says so.
if _, err = client.DeleteLock(ctx, nestedItemId.Key, nestedItemId.Label, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", nestedItemId.Key, nestedItemId.Label, err)
// Remove the lock, if any. We will put it back again if the model says so.
if _, err = client.DeleteLock(ctx, nestedItemId.Key, nestedItemId.Label, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", nestedItemId.Key, nestedItemId.Label, err)
}

if metadata.ResourceData.HasChange("tags") {
kv.Tags = tags.Expand(model.Tags)
}

if metadata.ResourceData.HasChange("locked") {
kv.Locked = pointer.To(model.Locked)
}

if metadata.ResourceData.HasChange("enabled") {
fv.Enabled = model.Enabled
}

if metadata.ResourceData.HasChange("description") {
fv.Description = model.Description
}

filters := make([]interface{}, 0)
filterChanged := false
timewindowFilters := make([]interface{}, 0)
targetingFilters := make([]interface{}, 0)
percentageFilter := PercentageFeatureFilter{}
if len(fv.Conditions.ClientFilters.Filters) > 0 {
for _, f := range fv.Conditions.ClientFilters.Filters {
switch f := f.(type) {
case TimewindowFeatureFilter:
twfp := f
timewindowFilters = append(timewindowFilters, twfp)
case TargetingFeatureFilter:
tfp := f
targetingFilters = append(targetingFilters, tfp)
case PercentageFeatureFilter:
pfp := f
percentageFilter = pfp
default:
return fmt.Errorf("while unmarshaling feature payload: unknown filter type %+v", f)
}
}
}

if metadata.ResourceData.HasChange("percentage_filter_value") {
filters = append(filters, PercentageFeatureFilter{
Name: PercentageFilterName,
Parameters: PercentageFilterParameters{Value: model.PercentageFilter},
})
filterChanged = true
} else {
filters = append(filters, percentageFilter)
}

if metadata.ResourceData.HasChange("targeting_filter") {
for _, tgtf := range model.TargetingFilters {
filters = append(filters, TargetingFeatureFilter{
Name: TargetingFilterName,
Parameters: TargetingFilterParameters{Audience: tgtf},
})
}
filterChanged = true
} else {
filters = append(filters, targetingFilters...)
}

if metadata.ResourceData.HasChange("timewindow_filter") {
for _, twf := range model.TimewindowFilters {
filters = append(filters, TimewindowFeatureFilter{
Name: TimewindowFilterName,
Parameters: twf,
})
}
filterChanged = true
} else {
filters = append(filters, timewindowFilters...)
}

if filterChanged {
fv.Conditions.ClientFilters.Filters = filters
}

valueBytes, err := json.Marshal(fv)
if err != nil {
return fmt.Errorf("while marshalling FeatureValue struct: %+v", err)
}
kv.Value = pointer.To(string(valueBytes))
if _, err = client.PutKeyValue(ctx, nestedItemId.Key, model.Label, &kv, "", ""); err != nil {
return err
}

if model.Locked {
if _, err = client.PutLock(ctx, nestedItemId.Key, model.Label, "", ""); err != nil {
return fmt.Errorf("while locking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
err = createOrUpdateFeature(ctx, client, model)
if err != nil {
return fmt.Errorf("while updating feature: %+v", err)
} else {
if _, err = client.DeleteLock(ctx, nestedItemId.Key, model.Label, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
}

Expand Down Expand Up @@ -442,74 +599,6 @@ func (k FeatureResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return validate.NestedItemId
}

func createOrUpdateFeature(ctx context.Context, client *appconfiguration.BaseClient, model FeatureResourceModel) error {
rawKey := model.Name
if model.Key != "" {
rawKey = model.Key
}
featureKey := fmt.Sprintf("%s/%s", FeatureKeyPrefix, rawKey)

entity := appconfiguration.KeyValue{
Key: utils.String(featureKey),
Label: utils.String(model.Label),
Tags: tags.Expand(model.Tags),
ContentType: utils.String(FeatureKeyContentType),
Locked: utils.Bool(model.Locked),
}

value := FeatureValue{
ID: model.Name,
Description: model.Description,
Enabled: model.Enabled,
}

value.Conditions.ClientFilters.Filters = make([]interface{}, 0)
if model.PercentageFilter > 0 {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, PercentageFeatureFilter{
Name: PercentageFilterName,
Parameters: PercentageFilterParameters{Value: model.PercentageFilter},
})
}

if len(model.TargetingFilters) > 0 {
for _, tgtf := range model.TargetingFilters {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, TargetingFeatureFilter{
Name: TargetingFilterName,
Parameters: TargetingFilterParameters{Audience: tgtf},
})
}
}

if len(model.TimewindowFilters) > 0 {
for _, twf := range model.TimewindowFilters {
value.Conditions.ClientFilters.Filters = append(value.Conditions.ClientFilters.Filters, TimewindowFeatureFilter{
Name: TimewindowFilterName,
Parameters: twf,
})
}
}

valueBytes, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("while marshalling FeatureValue struct: %+v", err)
}
entity.Value = utils.String(string(valueBytes))
if _, err = client.PutKeyValue(ctx, featureKey, model.Label, &entity, "", ""); err != nil {
return err
}

if model.Locked {
if _, err = client.PutLock(ctx, featureKey, model.Label, "", ""); err != nil {
return fmt.Errorf("while locking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
} else {
if _, err = client.DeleteLock(ctx, featureKey, model.Label, "", ""); err != nil {
return fmt.Errorf("while unlocking key/label pair %s/%s: %+v", model.Name, model.Label, err)
}
}

return nil
}
func (k FeatureResource) StateUpgraders() sdk.StateUpgradeData {
return sdk.StateUpgradeData{
SchemaVersion: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ func TestAccAppConfigurationFeature_basic(t *testing.T) {
})
}

func TestAccAppConfigurationFeature_percentFilter(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_app_configuration_feature", "test")
r := AppConfigurationFeatureResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.percentageFilter(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.basicPercentageFilter(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}
func TestAccAppConfigurationFeature_basicNoLabel(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_app_configuration_feature", "test")
r := AppConfigurationFeatureResource{}
Expand Down Expand Up @@ -253,6 +280,60 @@ resource "azurerm_app_configuration_feature" "test" {
`, t.template(data), data.RandomInteger)
}

func (t AppConfigurationFeatureResource) percentageFilter(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_app_configuration_feature" "test" {
configuration_store_id = azurerm_app_configuration.test.id
description = "test description"
name = "acctest-ackey-%d"
label = "acctest-ackeylabel-%[2]d"
enabled = true
percentage_filter_value = 50.65
timewindow_filter {
start = "2019-11-12T07:20:50.52Z"
end = "2019-11-13T07:20:50.52Z"
}
targeting_filter {
default_rollout_percentage = 39
users = ["random", "user"]
groups {
name = "testgroup"
rollout_percentage = 50
}
groups {
name = "testgroup2"
rollout_percentage = 30
}
}
}
`, t.template(data), data.RandomInteger)
}

func (t AppConfigurationFeatureResource) basicPercentageFilter(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_app_configuration_feature" "test" {
configuration_store_id = azurerm_app_configuration.test.id
description = "test description"
name = "acctest-ackey-%d"
label = "acctest-ackeylabel-%[2]d"
enabled = true
percentage_filter_value = 89.91
}
`, t.template(data), data.RandomInteger)
}

func (t AppConfigurationFeatureResource) basicNoLabel(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
Loading

0 comments on commit 4c0ff69

Please sign in to comment.