diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index e1a118d..3475713 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -2,6 +2,7 @@ package generate import ( "bytes" + "errors" "fmt" "go/format" "io" @@ -149,14 +150,16 @@ func (c *Creator) processTemplate(templateName, filePath string) error { // writeFormattedContentToFile formats the content and writes it to a file. func (c *Creator) writeFormattedContentToFile(filePath, content string) error { - formattedCode, err := format.Source([]byte(content)) - if err != nil { - log.Printf("provided content: %s", content) - return fmt.Errorf("error formatting code: %w", err) + var formattedCode []byte + var formatErr error + formattedCode, formatErr = format.Source([]byte(content)) + if formatErr != nil { + log.Printf("Failed to format target path: %s", filePath) + formattedCode = []byte(content) } formattedBuf := bytes.NewBuffer(formattedCode) - return c.createFileAndWriteContent(filePath, formattedBuf) + return errors.Join(formatErr, c.createFileAndWriteContent(filePath, formattedBuf)) } // createTerraformProviderFilePath returns a file path for a Terraform provider based on the provided suffix. diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 8e53636..1b4c93e 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -164,6 +164,7 @@ type SpecParam struct { Required bool `json:"required" yaml:"required"` Sensitive bool `json:"sensitive" yaml:"sensitive"` Length *SpecParamLength `json:"length" yaml:"length,omitempty"` + EnumValues map[string]*string `json:"enum_values" yaml:"enum_values,omitempty"` Count *SpecParamCount `json:"count" yaml:"count,omitempty"` Hashing *SpecParamHashing `json:"hashing" yaml:"hashing,omitempty"` Items *SpecParamItems `json:"items" yaml:"items,omitempty"` @@ -698,16 +699,24 @@ func schemaToSpec(object object.Object) (*Normalization, error) { if err != nil { return nil, err } - + enumValues := make(map[string]*string) switch spec := param.Spec.(type) { case *parameter.EnumSpec: constValues := make(map[string]*ConstValue) for _, elt := range spec.Values { + var constValue *string if elt.Const == "" { - continue + constValue = nil + } else { + constValue = &elt.Const } - constValues[elt.Const] = &ConstValue{ - Value: elt.Value, + + enumValues[elt.Value] = constValue + + if constValue != nil { + constValues[*constValue] = &ConstValue{ + Value: elt.Value, + } } } if len(constValues) > 0 { @@ -715,8 +724,8 @@ func schemaToSpec(object object.Object) (*Normalization, error) { Values: constValues, } } - } + specParam.EnumValues = enumValues spec.Spec.Params[param.Name] = specParam } diff --git a/pkg/translate/terraform_provider/funcs.go b/pkg/translate/terraform_provider/funcs.go index 1bd43ae..7ec8da7 100644 --- a/pkg/translate/terraform_provider/funcs.go +++ b/pkg/translate/terraform_provider/funcs.go @@ -741,8 +741,10 @@ type modifierCtx struct { } type validatorFunctionCtx struct { + Type string Function string Expressions []string + Values []string } type validatorCtx struct { @@ -959,6 +961,43 @@ func createSchemaSpecForParameter(schemaTyp schemaType, manager *imports.Manager }) } + for _, elt := range param.Spec.Params { + if elt.IsPrivateParameter() { + continue + } + + var functions []validatorFunctionCtx + if len(elt.EnumValues) > 0 && schemaTyp == schemaResource { + var values []string + for value := range elt.EnumValues { + values = append(values, value) + } + + functions = append(functions, validatorFunctionCtx{ + Type: "Values", + Function: "OneOf", + Values: values, + }) + } + + var validators *validatorCtx + if len(functions) > 0 { + typ := elt.ValidatorType() + validatorImport := fmt.Sprintf("github.com/hashicorp/terraform-plugin-framework-validators/%svalidator", typ) + manager.AddHashicorpImport(validatorImport, "") + validators = &validatorCtx{ + ListType: pascalCase(typ), + Package: fmt.Sprintf("%svalidator", typ), + Functions: functions, + } + } + + attributes = append(attributes, createSchemaAttributeForParameter(schemaTyp, manager, packageName, elt, validators)) + } + + // Generating schema validation for variants. By default, ExactlyOneOf validation + // is performed, unless XML API allows for no variant to be provided, in which case + // validation is performed by ConflictsWith. validatorFn := "ExactlyOneOf" var expressions []string for _, elt := range param.Spec.OneOf { @@ -973,18 +1012,15 @@ func createSchemaSpecForParameter(schemaTyp schemaType, manager *imports.Manager expressions = append(expressions, fmt.Sprintf(`path.MatchRelative().AtParent().AtName("%s")`, elt.Name.Underscore)) } - for _, elt := range param.Spec.Params { - if elt.IsPrivateParameter() { - continue - } - attributes = append(attributes, createSchemaAttributeForParameter(schemaTyp, manager, packageName, elt, nil)) + var variantFns []validatorFunctionCtx + if len(expressions) > 0 { + variantFns = append(variantFns, validatorFunctionCtx{ + Type: "Expressions", + Function: validatorFn, + Expressions: expressions, + }) } - functions := []validatorFunctionCtx{{ - Function: validatorFn, - Expressions: expressions, - }} - var idx int for _, elt := range param.Spec.OneOf { if elt.IsPrivateParameter() { @@ -992,14 +1028,15 @@ func createSchemaSpecForParameter(schemaTyp schemaType, manager *imports.Manager } var validators *validatorCtx - if idx == 0 { + if idx == 0 && schemaTyp == schemaResource && len(variantFns) > 0 { + log.Printf("variantFns: %v, Name: %s", variantFns, elt.Name) typ := elt.ValidatorType() validatorImport := fmt.Sprintf("github.com/hashicorp/terraform-plugin-framework-validators/%svalidator", typ) manager.AddHashicorpImport(validatorImport, "") validators = &validatorCtx{ ListType: pascalCase(typ), Package: fmt.Sprintf("%svalidator", typ), - Functions: functions, + Functions: variantFns, } } attributes = append(attributes, createSchemaAttributeForParameter(schemaTyp, manager, packageName, elt, validators)) @@ -1043,8 +1080,34 @@ func createSchemaSpecForParameter(schemaTyp schemaType, manager *imports.Manager continue } + var functions []validatorFunctionCtx + if len(elt.EnumValues) > 0 && schemaTyp == schemaResource { + var values []string + for value := range elt.EnumValues { + values = append(values, value) + } + + functions = append(functions, validatorFunctionCtx{ + Type: "Values", + Function: "OneOf", + Values: values, + }) + } + + var validators *validatorCtx + if len(functions) > 0 { + typ := elt.ValidatorType() + validatorImport := fmt.Sprintf("github.com/hashicorp/terraform-plugin-framework-validators/%svalidator", typ) + manager.AddHashicorpImport(validatorImport, "") + validators = &validatorCtx{ + ListType: pascalCase(typ), + Package: fmt.Sprintf("%svalidator", typ), + Functions: functions, + } + } + if elt.Type == "" || (elt.Type == "list" && elt.Items.Type == "entry") { - schemas = append(schemas, createSchemaSpecForParameter(schemaTyp, manager, structName, packageName, elt, nil)...) + schemas = append(schemas, createSchemaSpecForParameter(schemaTyp, manager, structName, packageName, elt, validators)...) } } @@ -1059,7 +1122,7 @@ func createSchemaSpecForParameter(schemaTyp schemaType, manager *imports.Manager validators := &validatorCtx{ ListType: "Object", Package: "objectvalidator", - Functions: functions, + Functions: variantFns, } schemas = append(schemas, createSchemaSpecForParameter(schemaTyp, manager, structName, packageName, elt, validators)...) } @@ -1422,7 +1485,34 @@ func createSchemaSpecForNormalization(resourceTyp properties.ResourceType, schem if elt.IsPrivateParameter() { continue } - attributes = append(attributes, createSchemaAttributeForParameter(schemaTyp, manager, packageName, elt, nil)) + + var functions []validatorFunctionCtx + if len(elt.EnumValues) > 0 && schemaTyp == schemaResource { + var values []string + for value := range elt.EnumValues { + values = append(values, value) + } + + functions = append(functions, validatorFunctionCtx{ + Type: "Values", + Function: "OneOf", + Values: values, + }) + } + + var validators *validatorCtx + if len(functions) > 0 { + typ := elt.ValidatorType() + validatorImport := fmt.Sprintf("github.com/hashicorp/terraform-plugin-framework-validators/%svalidator", typ) + manager.AddHashicorpImport(validatorImport, "") + validators = &validatorCtx{ + ListType: pascalCase(typ), + Package: fmt.Sprintf("%svalidator", typ), + Functions: functions, + } + } + + attributes = append(attributes, createSchemaAttributeForParameter(schemaTyp, manager, packageName, elt, validators)) schemas = append(schemas, createSchemaSpecForParameter(schemaTyp, manager, structName, packageName, elt, nil)...) } @@ -1439,18 +1529,24 @@ func createSchemaSpecForNormalization(resourceTyp properties.ResourceType, schem expressions = append(expressions, fmt.Sprintf(`path.MatchRelative().AtParent().AtName("%s")`, elt.Name.Underscore)) } - functions := []validatorFunctionCtx{{ - Function: validatorFn, - Expressions: expressions, - }} + var functions []validatorFunctionCtx + + if len(expressions) > 0 { + functions = append(functions, validatorFunctionCtx{ + Type: "Expressions", + Function: validatorFn, + Expressions: expressions, + }) + } var idx int for _, elt := range spec.Spec.OneOf { if elt.IsPrivateParameter() { continue } + var validators *validatorCtx - if idx == 0 { + if idx == 0 && schemaTyp == schemaResource && len(functions) > 0 { typ := elt.ValidatorType() validatorImport := fmt.Sprintf("github.com/hashicorp/terraform-plugin-framework-validators/%svalidator", typ) manager.AddHashicorpImport(validatorImport, "") @@ -1483,11 +1579,20 @@ const renderSchemaTemplate = ` {{ $package := .Package }} Validators: []validator.{{ .ListType }}{ {{- range .Functions }} + {{- if eq .Type "Expressions" }} {{ $package }}.{{ .Function }}(path.Expressions{ - {{- range .Expressions }} + {{- range .Expressions }} {{ . }}, - {{- end }} + {{- end }} + }...), + + {{- else if eq .Type "Values" }} + {{ $package }}.{{ .Function }}([]string{ + {{- range .Values }} + {{ . }}, + {{- end }} }...), + {{- end }} {{- end }} }, {{- end }} @@ -1557,11 +1662,19 @@ const renderSchemaTemplate = ` {{ $package := .Package }} Validators: []validator.{{ .ListType }}{ {{- range .Functions }} + {{- if eq .Type "Expressions" }} {{ $package }}.{{ .Function }}(path.Expressions{ - {{- range .Expressions }} + {{- range .Expressions }} {{ . }}, - {{- end }} + {{- end }} }...), + {{- else if eq .Type "Values" }} + {{ $package }}.{{ .Function }}([]string{ + {{- range .Values }} + "{{ . }}", + {{- end }} + }...), + {{- end }} {{- end }} }, {{- end }} diff --git a/pkg/translate/terraform_provider/terraform_provider_file.go b/pkg/translate/terraform_provider/terraform_provider_file.go index 3166cf9..9256b55 100644 --- a/pkg/translate/terraform_provider/terraform_provider_file.go +++ b/pkg/translate/terraform_provider/terraform_provider_file.go @@ -72,7 +72,6 @@ func (g *GenerateTerraformProvider) updateProviderFile(renderedTemplate *strings case "ProviderFile": terraformProvider.Code = renderedTemplate default: - log.Printf("updateProviderFile() renderedTemplate: %d", renderedTemplate.Len()) if _, err := terraformProvider.Code.WriteString(renderedTemplate.String()); err != nil { return fmt.Errorf("error writing %s template: %v", resourceType, err) } diff --git a/specs/objects/custom-url-category.yaml b/specs/objects/custom-url-category.yaml index eefe875..a0fc2e8 100644 --- a/specs/objects/custom-url-category.yaml +++ b/specs/objects/custom-url-category.yaml @@ -137,9 +137,17 @@ spec: items: type: string - name: type - type: string + type: enum profiles: - xpath: ["type"] + validators: + - type: values + spec: + values: ["URL List", "Category Match"] + spec: + values: + - value: URL List + - value: Category Match - name: disable-override type: bool profiles: