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

[New Resource]: DataZone Form Type #38746

Merged
merged 10 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/38746.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_datazone_form_type
```
14 changes: 9 additions & 5 deletions internal/service/datazone/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ package datazone
var (
ResourceDomain = newResourceDomain
ResourceEnvironmentBlueprintConfiguration = newResourceEnvironmentBlueprintConfiguration
IsResourceMissing = isResourceMissing
ResourceProject = newResourceProject
ResourceEnvironmentProfile = newResourceEnvironmentProfile
FindEnvironmentProfileByID = findEnvironmentProfileByID
ResourceFormType = newResourceFormType
ResourceGlossary = newResourceGlossary
FindGlossaryByID = findGlossaryByID
ResourceGlossaryTerm = newResourceGlossaryTerm
FindGlossaryTermByID = findGlossaryTermByID
ResourceProject = newResourceProject

FindEnvironmentProfileByID = findEnvironmentProfileByID
FindFormTypeByID = findFormTypeByID
FindGlossaryByID = findGlossaryByID
FindGlossaryTermByID = findGlossaryTermByID

IsResourceMissing = isResourceMissing
)
346 changes: 346 additions & 0 deletions internal/service/datazone/form_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package datazone

import (
"context"
"errors"
"strings"
"time"

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/datazone"
awstypes "github.com/aws/aws-sdk-go-v2/service/datazone/types"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-provider-aws/internal/create"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/framework"
"github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/names"
)

// @FrameworkResource("aws_datazone_form_type", name="Form Type")
func newResourceFormType(_ context.Context) (resource.ResourceWithConfigure, error) {
r := &resourceFormType{}
r.SetDefaultCreateTimeout(30 * time.Second)

return r, nil
}

const (
ResNameFormType = "Form Type"
)

type resourceFormType struct {
framework.ResourceWithConfigure
framework.WithTimeouts
framework.WithNoUpdate
}

func (r *resourceFormType) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "aws_datazone_form_type"
}
func (r *resourceFormType) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
names.AttrCreatedAt: schema.StringAttribute{
CustomType: timetypes.RFC3339Type{},
Computed: true,
},
"created_by": schema.StringAttribute{
Computed: true,
},
names.AttrDescription: schema.StringAttribute{
Optional: true,
Validators: []validator.String{
stringvalidator.LengthBetween(1, 2048),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"domain_identifier": schema.StringAttribute{
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(regexache.MustCompile(`^dzd[-_][a-zA-Z0-9_-]{1,36}$`), "^dzd[-_][a-zA-Z0-9_-]{1,36}$"),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"imports": schema.ListAttribute{
CustomType: fwtypes.NewListNestedObjectTypeOf[importData](ctx),
Computed: true,
},
names.AttrName: schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"origin_domain_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"origin_project_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"owning_project_identifier": schema.StringAttribute{
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(regexache.MustCompile(`^[a-zA-Z0-9_-]{1,36}$`), "^[a-zA-Z0-9_-]{1,36}$"),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"revision": schema.StringAttribute{
Computed: true,
},
names.AttrStatus: schema.StringAttribute{
CustomType: fwtypes.StringEnumType[awstypes.FormTypeStatus](),
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
stringplanmodifier.UseStateForUnknown(),
},
},
},
Blocks: map[string]schema.Block{
"model": schema.ListNestedBlock{
CustomType: fwtypes.NewListNestedObjectTypeOf[modelData](ctx),
Validators: []validator.List{
listvalidator.SizeAtMost(1),
listvalidator.IsRequired(),
},
PlanModifiers: []planmodifier.List{
listplanmodifier.RequiresReplace(),
},
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"smithy": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
},
},
names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{
Create: true,
}),
},
}
}

func (r *resourceFormType) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
conn := r.Meta().DataZoneClient(ctx)

var plan resourceFormTypeData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

in := datazone.CreateFormTypeInput{}
resp.Diagnostics.Append(flex.Expand(ctx, plan, &in)...)
if resp.Diagnostics.HasError() {
return
}

out, err := conn.CreateFormType(ctx, &in)
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.DataZone, create.ErrActionCreating, ResNameFormType, plan.Name.String(), err),
err.Error(),
)
return
}
if out == nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.DataZone, create.ErrActionCreating, ResNameFormType, plan.Name.String(), nil),
errors.New("empty output").Error(),
)
return
}

createTimeout := r.CreateTimeout(ctx, plan.Timeouts)
outputRaws, err := tfresource.RetryWhenNotFound(ctx, createTimeout, func() (interface{}, error) {
return findFormTypeByID(ctx, conn, *out.DomainId, *out.Name, *out.Revision)
})
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.DataZone, create.ErrActionCreating, ResNameFormType, plan.Name.String(), err),
err.Error(),
)
return
}

output := outputRaws.(*datazone.GetFormTypeOutput)
option := flex.WithIgnoredFieldNames([]string{"Model"})
resp.Diagnostics.Append(flex.Flatten(ctx, output, &plan, option)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *resourceFormType) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
conn := r.Meta().DataZoneClient(ctx)

var state resourceFormTypeData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

out, err := findFormTypeByID(ctx, conn, state.DomainIdentifier.ValueString(), state.Name.ValueString(), state.Revision.ValueString())
if tfresource.NotFound(err) {
resp.State.RemoveResource(ctx)
return
}
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.DataZone, create.ErrActionSetting, ResNameFormType, state.Name.String(), err),
err.Error(),
)
return
}
option := flex.WithIgnoredFieldNames([]string{"Model"})

resp.Diagnostics.Append(flex.Flatten(ctx, out, &state, option)...)
if resp.Diagnostics.HasError() {
return
}

state.OwningProjectIdentifier = flex.StringToFramework(ctx, out.OwningProjectId)

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *resourceFormType) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
conn := r.Meta().DataZoneClient(ctx)

var state resourceFormTypeData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

in := &datazone.DeleteFormTypeInput{
DomainIdentifier: state.DomainIdentifier.ValueStringPointer(),
FormTypeIdentifier: state.Name.ValueStringPointer(),
}

_, err := conn.DeleteFormType(ctx, in)
if err != nil {
if errs.IsA[*awstypes.ResourceNotFoundException](err) || errs.IsA[*awstypes.AccessDeniedException](err) {
return
}
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.DataZone, create.ErrActionDeleting, ResNameFormType, state.Name.String(), err),
err.Error(),
)
return
}
}
func (r *resourceFormType) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
parts := strings.Split(req.ID, ",")

if len(parts) != 3 {
resp.Diagnostics.AddError("Resource Import Invalid ID", `Unexpected format for import ID, use: "DomainIdentifier:Name,Revision"`)
return
}

resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("domain_identifier"), parts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root(names.AttrName), parts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("revision"), parts[2])...)
}

func findFormTypeByID(ctx context.Context, conn *datazone.Client, domainId string, name string, revision string) (*datazone.GetFormTypeOutput, error) {
in := &datazone.GetFormTypeInput{
DomainIdentifier: aws.String(domainId),
FormTypeIdentifier: aws.String(name),
Revision: aws.String(revision),
}

out, err := conn.GetFormType(ctx, in)
if errs.IsA[*awstypes.ResourceNotFoundException](err) || errs.IsA[*awstypes.AccessDeniedException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: in,
}
}

if err != nil {
return nil, err
}

if out == nil {
return nil, tfresource.NewEmptyResultError(in)
}

return out, nil
}

func (m modelData) Expand(ctx context.Context) (result any, diags diag.Diagnostics) {
switch {
case !m.Smithy.IsNull():

var r awstypes.ModelMemberSmithy

r.Value = m.Smithy.ValueString()
return &r, diags
}
return
}

type resourceFormTypeData struct {
CreatedAt timetypes.RFC3339 `tfsdk:"created_at"`
CreatedBy types.String `tfsdk:"created_by"`
Description types.String `tfsdk:"description"`
DomainIdentifier types.String `tfsdk:"domain_identifier"`
Imports fwtypes.ListNestedObjectValueOf[importData] `tfsdk:"imports"`
Model fwtypes.ListNestedObjectValueOf[modelData] `tfsdk:"model"`
Name types.String `tfsdk:"name"`
OriginDomainId types.String `tfsdk:"origin_domain_id"`
OriginProjectId types.String `tfsdk:"origin_project_id"`
OwningProjectIdentifier types.String `tfsdk:"owning_project_identifier"`
Revision types.String `tfsdk:"revision"`
Status fwtypes.StringEnum[awstypes.FormTypeStatus] `tfsdk:"status"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}

type modelData struct {
Smithy types.String `tfsdk:"smithy"`
}

type importData struct {
Name types.String `tfsdk:"name"`
Revision types.String `tfsdk:"revision"`
}
Loading
Loading