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

Verify enum values in terraform #179

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
13 changes: 8 additions & 5 deletions pkg/generate/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package generate

import (
"bytes"
"errors"
"fmt"
"go/format"
"io"
Expand Down Expand Up @@ -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.
Expand Down
19 changes: 14 additions & 5 deletions pkg/properties/normalized.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -698,25 +699,33 @@ 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 {
consts[param.Name] = &Const{
Values: constValues,
}
}

}
specParam.EnumValues = enumValues
spec.Spec.Params[param.Name] = specParam
}

Expand Down
161 changes: 137 additions & 24 deletions pkg/translate/terraform_provider/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,8 +741,10 @@ type modifierCtx struct {
}

type validatorFunctionCtx struct {
Type string
Function string
Expressions []string
Values []string
}

type validatorCtx struct {
Expand Down Expand Up @@ -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 {
Expand All @@ -973,33 +1012,31 @@ 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() {
continue
}

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))
Expand Down Expand Up @@ -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)...)
}
}

Expand All @@ -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)...)
}
Expand Down Expand Up @@ -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)...)
}

Expand All @@ -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, "")
Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
10 changes: 9 additions & 1 deletion specs/objects/custom-url-category.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down