Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_api_management_api: split create and update method #28271

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

wuxu92
Copy link
Contributor

@wuxu92 wuxu92 commented Dec 13, 2024

Community Note

  • Please vote on this PR by adding a 👍 reaction to the original PR to help the community and maintainers prioritize for review
  • Please do not leave comments along the lines of "+1", "me too" or "any updates", they generate extra noise for PR followers and do not help prioritize for review

Description

Split Update method from CreateUpdate method, and an update test for it.

PR Checklist

  • I have followed the guidelines in our Contributing Documentation.
  • I have checked to ensure there aren't other open Pull Requests for the same update/change.
  • I have checked if my changes close any open issues. If so please include appropriate closing keywords below.
  • I have updated/added Documentation as required written in a helpful and kind way to assist users that may be unfamiliar with the resource / data source.
  • I have used a meaningful PR title to help maintainers and other users understand this change and help prevent duplicate work.
    For example: “resource_name_here - description of change e.g. adding property new_property_name_here

Testing

  • My submission includes Test coverage as described in the Contribution Guide and the tests pass. (if this is not possible for any reason, please include details of why you did or could not add test coverage)
--- PASS: TestAccApiManagementApi_completeUpdate (252.61s)
PASS

image

Copy link
Member

@mbfrahry mbfrahry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wuxu92, this PR is nearly there but there is an issue with the update because we're using a new payload instead of the existing one. I've left some comments below but happy to answer any questions


// If import is used, we need to send properties to Azure API in two operations.
// First we execute import and then updated the other props.
if vs, hasImport := d.GetOk("import"); hasImport {
importVs := vs.([]interface{})
if importVs := d.Get("import").([]interface{}); len(importVs) > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should confirm we can cast to .([]interface{}) to prevent a potential panic

Suggested change
if importVs := d.Get("import").([]interface{}); len(importVs) > 0 {
if importVs, ok := d.Get("import").([]interface{}); ok && len(importVs) > 0 {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get will return the zero value of the schema if the field is not set. Anyway, I'd add the check here to make it clear.

if result.Value == nil && schema != nil {
result.Value = result.ValueOrZero(schema)
}

}
}

prop := &api.ApiCreateOrUpdateProperties{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestAccApiManagementApi_importUpdate is failing because we're building a new payload rather than using the existing one.

Copy link
Contributor Author

@wuxu92 wuxu92 Dec 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I've updated the Update method with existing payload and the acctest updated. some fields can be set by tf configuration or by import block. so I added an ignore_changes to this acc test.

--- PASS: TestAccApiManagementApi_importUpdate (336.19s)
PASS```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to set every variable in a new struct or could we just use existing and change any values through d.HasChange?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data type of existing is ApiContractProperties which is not the same as ApiCreateOrUpdateProperties. so a copy is required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh shoot. Thanks for the added context!

path = "butter-parser-update"
protocols = ["https"]
revision = "3"
description = "What is my purpose? You parse butter update."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is changing every updatable attribute, we'll miss a bug in the current update method. I encourage you to keep this value the same and run this test again to see what happens

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned, the update method is now working with the existing payload and has been tested successfully.

--- PASS: TestAccApiManagementApi_completeUpdate (389.37s)
PASS

@wuxu92
Copy link
Contributor Author

wuxu92 commented Jan 28, 2025

@mbfrahry Could you review this PR again? These comments should have been resolved.

Copy link
Member

@mbfrahry mbfrahry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience on this review. There are some things that won't work quite right the way it's written that I've documented. Let me know if you have any questions

return fmt.Errorf("creating/updating %s: %+v", *id, err)
}

d.SetId(id.ID())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to reset the ID after we update as it should be the same ID as when we set it during Create. This could cause issues if the IDs between Create and Update aren't the same for whatever reason

}

func expandApiManagementApiImport(importVs []interface{}, apiType api.ApiType, soapApiType api.SoapApiType, path, serviceUrl, version, versionSetId string) api.ApiCreateOrUpdateParameter {
importV := importVs[0].(map[string]interface{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There aren't any checks that len(importVs) isn't 0 or that importVs[0] isn't nil. We'll want to add those to prevent a potential crash

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated. also update the create method with this expand method.


wsdlSelectorVs := importV["wsdl_selector"].([]interface{})
if len(wsdlSelectorVs) > 0 {
wsdlSelectorV := wsdlSelectorVs[0].(map[string]interface{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here, we want to check that wsdlSelectorVs[0] isn't nil

}

if d.HasChange("version_set_id") {
prop.ApiVersionSetId = &versionSetId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
prop.ApiVersionSetId = &versionSetId
prop.ApiVersionSetId = pointer.To(versionSetId)

sourceApiId := d.Get("source_api_id").(string)
serviceUrl := d.Get("service_url").(string)

if version != "" && versionSetId == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can reduce code duplication and get the following checks checked at plan time rather than apply time by using doing CustomizeDiff

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

Plan: 2 to add, 0 to change, 1 to destroy.
│ Error: 1 error occurred:
│       * setting `version` without the required `version_set_id`
│                


Plan: 2 to add, 0 to change, 1 to destroy.
╷
│ Error: 1 error occurred:
│       * `display_name`, `protocols` are required when `source_api_id` is not set

Comment on lines +607 to +572
subscriptionKeyParameterNamesRaw := d.Get("subscription_key_parameter_names").([]interface{})
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(subscriptionKeyParameterNamesRaw)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
subscriptionKeyParameterNamesRaw := d.Get("subscription_key_parameter_names").([]interface{})
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(subscriptionKeyParameterNamesRaw)
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(d.Get("subscription_key_parameter_names").([]interface{}))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

oAuth2AuthorizationSettingsRaw := d.Get("oauth2_authorization").([]interface{})
oAuth2AuthorizationSettings := expandApiManagementOAuth2AuthenticationSettingsContract(oAuth2AuthorizationSettingsRaw)
authenticationSettings.OAuth2 = oAuth2AuthorizationSettings
prop.AuthenticationSettings = authenticationSettings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way this is written will reset openid_authentication if it doesn't change as it's setting oauth2_authorization into an empty AuthenticationSettingsContract

We should modify the existing AuthenticationSettings rather than creating a new one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Updated.

openIDAuthorizationSettingsRaw := d.Get("openid_authentication").([]interface{})
openIDAuthorizationSettings := expandApiManagementOpenIDAuthenticationSettingsContract(openIDAuthorizationSettingsRaw)
authenticationSettings.Openid = openIDAuthorizationSettings
prop.AuthenticationSettings = authenticationSettings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the vice versa, this will reset oauth2_authorization if oauth2_authorization hasn't changed

We should modify the existing AuthenticationSettings rather than creating a new one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated with above

Comment on lines 627 to 628
contactInfoRaw := d.Get("contact").([]interface{})
prop.Contact = expandApiManagementApiContact(contactInfoRaw)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
contactInfoRaw := d.Get("contact").([]interface{})
prop.Contact = expandApiManagementApiContact(contactInfoRaw)
prop.Contact = expandApiManagementApiContact(d.Get("contact").([]interface{}))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 632 to 633
licenseInfoRaw := d.Get("license").([]interface{})
prop.License = expandApiManagementApiLicense(licenseInfoRaw)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
licenseInfoRaw := d.Get("license").([]interface{})
prop.License = expandApiManagementApiLicense(licenseInfoRaw)
prop.License = expandApiManagementApiLicense(d.Get("license").([]interface{}))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

@wuxu92
Copy link
Contributor Author

wuxu92 commented Feb 14, 2025

@mbfrahry Thanks for the comments! I updated the branch with the main and fixed the corresponding code.

Copy link
Member

@mbfrahry mbfrahry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wuxu92, thanks for taking the time to go over my review. I left a couple more comments

ApiVersion: existing.ApiVersion,
ApiVersionSetId: existing.ApiVersionSetId,
TermsOfServiceURL: existing.TermsOfServiceURL,
Type: existing.Type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look like ApiType didn't get copied over. I just want to make sure that was intentional


CustomizeDiff: pluginsdk.CustomDiffWithAll(
pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error {
if d.Get("version").(string) != "" && d.Get("version_set_id").(string) == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you saw the errors around

 * setting `version` without the required `version_set_id`

That's because version_set_id is interpolated with azurerm_api_management_api_version_set.test.id and Terraform doesn't know what an interpolated value is at plan time so it marks it as an empty string.

What you can instead do is use d.GetRawConfig().AsValueMap()["version_set_id"].IsNull() which Terraform can look to see if it's interpolated or not.

You can make similar changes to the check below as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants