Skip to content

Commit

Permalink
azapi_* - support payload
Browse files Browse the repository at this point in the history
  • Loading branch information
ms-henglu committed Apr 9, 2024
1 parent 9c82789 commit fb551a7
Show file tree
Hide file tree
Showing 15 changed files with 1,523 additions and 61 deletions.
100 changes: 86 additions & 14 deletions internal/services/azapi_data_plane_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Azure/terraform-provider-azapi/internal/clients"
"github.com/Azure/terraform-provider-azapi/internal/locks"
"github.com/Azure/terraform-provider-azapi/internal/services/defaults"
"github.com/Azure/terraform-provider-azapi/internal/services/dynamic"
"github.com/Azure/terraform-provider-azapi/internal/services/myplanmodifier"
"github.com/Azure/terraform-provider-azapi/internal/services/myvalidator"
"github.com/Azure/terraform-provider-azapi/internal/services/parse"
Expand All @@ -34,11 +35,13 @@ type DataPlaneResourceModel struct {
ParentID types.String `tfsdk:"parent_id"`
Type types.String `tfsdk:"type"`
Body types.String `tfsdk:"body"`
Payload types.Dynamic `tfsdk:"payload"`
IgnoreCasing types.Bool `tfsdk:"ignore_casing"`
IgnoreMissingProperty types.Bool `tfsdk:"ignore_missing_property"`
ResponseExportValues types.List `tfsdk:"response_export_values"`
Locks types.List `tfsdk:"locks"`
Output types.String `tfsdk:"output"`
OutputPayload types.Dynamic `tfsdk:"output_payload"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}

Expand All @@ -49,6 +52,7 @@ type DataPlaneResource struct {
var _ resource.Resource = &DataPlaneResource{}
var _ resource.ResourceWithConfigure = &DataPlaneResource{}
var _ resource.ResourceWithModifyPlan = &DataPlaneResource{}
var _ resource.ResourceWithValidateConfig = &DataPlaneResource{}

func (r *DataPlaneResource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
if v, ok := request.ProviderData.(*clients.Client); ok {
Expand Down Expand Up @@ -108,6 +112,10 @@ func (r *DataPlaneResource) Schema(ctx context.Context, request resource.SchemaR
},
},

"payload": schema.DynamicAttribute{
Optional: true,
},

"ignore_casing": schema.BoolAttribute{
Optional: true,
Computed: true,
Expand Down Expand Up @@ -139,6 +147,10 @@ func (r *DataPlaneResource) Schema(ctx context.Context, request resource.SchemaR
"output": schema.StringAttribute{
Computed: true,
},

"output_payload": schema.DynamicAttribute{
Computed: true,
},
},

Blocks: map[string]schema.Block{
Expand All @@ -151,6 +163,23 @@ func (r *DataPlaneResource) Schema(ctx context.Context, request resource.SchemaR
}
}

func (r *DataPlaneResource) ValidateConfig(ctx context.Context, request resource.ValidateConfigRequest, response *resource.ValidateConfigResponse) {
var config *DataPlaneResourceModel
if response.Diagnostics.Append(request.Config.Get(ctx, &config)...); response.Diagnostics.HasError() {
return
}
// destroy doesn't need to modify plan
if config == nil {
return
}

// can't specify both body and payload
if !config.Body.IsNull() && !config.Payload.IsNull() {
response.Diagnostics.AddError("Invalid config", "can't specify both body and payload")
return
}
}

func (r *DataPlaneResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) {
var config, plan, state *DataPlaneResourceModel
response.Diagnostics.Append(request.Config.Get(ctx, &config)...)
Expand All @@ -165,8 +194,10 @@ func (r *DataPlaneResource) ModifyPlan(ctx context.Context, request resource.Mod
return
}

if state == nil || !plan.ResponseExportValues.Equal(state.ResponseExportValues) || utils.NormalizeJson(plan.Body.ValueString()) != utils.NormalizeJson(state.Body.ValueString()) {
if state == nil || !plan.ResponseExportValues.Equal(state.ResponseExportValues) || utils.NormalizeJson(plan.Body.ValueString()) != utils.NormalizeJson(state.Body.ValueString()) || !plan.Payload.Equal(state.Payload) {
plan.Output = types.StringUnknown()
plan.OutputPayload = basetypes.NewDynamicUnknown()
response.Diagnostics.Append(response.Plan.Set(ctx, plan)...)
}
}

Expand Down Expand Up @@ -212,11 +243,24 @@ func (r *DataPlaneResource) CreateUpdate(ctx context.Context, plan tfsdk.Plan, s
}
}

body := make(map[string]interface{})
err = json.Unmarshal([]byte(model.Body.ValueString()), &body)
if err != nil {
diagnostics.AddError("Invalid configuration", fmt.Sprintf(`The argument "body" is invalid, value: %s, error: %s`, model.Body.ValueString(), err))
return
var body map[string]interface{}
switch {
case !model.Payload.IsNull():
out, err := expandPayload(model.Payload)
if err != nil {
diagnostics.AddError("Invalid payload", err.Error())
return
}
body = out
case !model.Body.IsNull():
bodyValueString := model.Body.ValueString()
err := json.Unmarshal([]byte(bodyValueString), &body)
if err != nil {
diagnostics.AddError("Invalid JSON string", fmt.Sprintf(`The argument "body" is invalid: value: %s, err: %+v`, model.Body.ValueString(), err))
return
}
default:
body = map[string]interface{}{}
}

for _, id := range AsStringList(model.Locks) {
Expand All @@ -232,6 +276,7 @@ func (r *DataPlaneResource) CreateUpdate(ctx context.Context, plan tfsdk.Plan, s

model.ID = basetypes.NewStringValue(id.ID())
model.Output = basetypes.NewStringValue(flattenOutput(responseBody, AsStringList(model.ResponseExportValues)))
model.OutputPayload = types.DynamicValue(flattenOutputPayload(responseBody, AsStringList(model.ResponseExportValues)))

diagnostics.Append(state.Set(ctx, model)...)
}
Expand Down Expand Up @@ -269,29 +314,56 @@ func (r *DataPlaneResource) Read(ctx context.Context, request resource.ReadReque
return
}

bodyJson := model.Body.ValueString()
requestBody := make(map[string]interface{})
err = json.Unmarshal([]byte(bodyJson), &requestBody)
if err != nil && bodyJson != "" {
response.Diagnostics.AddError("Invalid configuration", fmt.Sprintf(`The argument "body" is invalid, value: %s, error: %s`, bodyJson, err))
return
var requestBody map[string]interface{}
var useBody bool
switch {
case !model.Payload.IsNull():
useBody = false
out, err := expandPayload(model.Payload)
if err != nil {
response.Diagnostics.AddError("Invalid payload", err.Error())
return
}
requestBody = out
case !model.Body.IsNull():
useBody = true
bodyValueString := model.Body.ValueString()
err := json.Unmarshal([]byte(bodyValueString), &requestBody)
if err != nil {
response.Diagnostics.AddError("Invalid JSON string", fmt.Sprintf(`The argument "body" is invalid: value: %s, err: %+v`, model.Body.ValueString(), err))
return
}
default:
requestBody = map[string]interface{}{}
}

option := utils.UpdateJsonOption{
IgnoreCasing: model.IgnoreCasing.ValueBool(),
IgnoreMissingProperty: model.IgnoreMissingProperty.ValueBool(),
}
data, err := json.Marshal(utils.UpdateObject(requestBody, responseBody, option))
body := utils.UpdateObject(requestBody, responseBody, option)

data, err := json.Marshal(body)
if err != nil {
response.Diagnostics.AddError("Invalid body", err.Error())
return
}
if useBody {
model.Body = types.StringValue(string(data))
} else {
payload, err := dynamic.FromJSON(data, model.Payload.UnderlyingValue().Type(ctx))
if err != nil {
response.Diagnostics.AddError("Invalid payload", err.Error())
return
}
model.Payload = payload
}

model.Body = basetypes.NewStringValue(string(data))
model.Name = basetypes.NewStringValue(id.Name)
model.ParentID = basetypes.NewStringValue(id.ParentId)
model.Type = basetypes.NewStringValue(fmt.Sprintf("%s@%s", id.AzureResourceType, id.ApiVersion))
model.Output = basetypes.NewStringValue(flattenOutput(responseBody, AsStringList(model.ResponseExportValues)))
model.OutputPayload = types.DynamicValue(flattenOutputPayload(responseBody, AsStringList(model.ResponseExportValues)))

response.Diagnostics.Append(response.State.Set(ctx, model)...)
}
Expand Down
64 changes: 64 additions & 0 deletions internal/services/azapi_data_plane_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ func TestAccDataPlaneResource_appConfigKeyValues(t *testing.T) {
})
}

func TestAccDataPlaneResource_dynamicSchema(t *testing.T) {
data := acceptance.BuildTestData(t, "azapi_data_plane_resource", "test")
r := DataPlaneResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.dynamicSchema(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
})
}

func TestAccDataPlaneResource_purviewClassification(t *testing.T) {
data := acceptance.BuildTestData(t, "azapi_data_plane_resource", "test")
r := DataPlaneResource{}
Expand Down Expand Up @@ -148,6 +162,56 @@ resource "azapi_data_plane_resource" "test" {
`, data.LocationPrimary, data.RandomString)
}

func (r DataPlaneResource) dynamicSchema(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "acctest%[2]s"
location = "%[1]s"
}
resource "azurerm_purview_account" "example" {
name = "acctest%[2]s"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
identity {
type = "SystemAssigned"
}
}
resource "azapi_data_plane_resource" "test" {
type = "Microsoft.Purview/accounts/Scanning/classificationrules@2022-07-01-preview"
parent_id = replace(azurerm_purview_account.example.scan_endpoint, "https://", "")
name = "acctest%[2]s"
payload = {
kind = "Custom"
properties = {
description = "Let's put a cool desc here"
classificationName = "MICROSOFT.FINANCIAL.AUSTRALIA.BANK_ACCOUNT_NUMBER"
columnPatterns = [
{
pattern = "^data$"
kind = "Regex"
}
]
dataPatterns = [
{
pattern = "^[0-9]{2}-[0-9]{4}-[0-9]{6}-[0-9]{3}$"
kind = "Regex"
}
]
minimumPercentageMatch = 60
ruleStatus = "Enabled"
}
}
}
`, data.LocationPrimary, data.RandomString)
}

func (r DataPlaneResource) purviewClassification(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
Loading

0 comments on commit fb551a7

Please sign in to comment.