diff --git a/pkg/sdk/poc/generator/template_executors.go b/pkg/sdk/poc/generator/template_executors.go index 8f315b2000..00b4678410 100644 --- a/pkg/sdk/poc/generator/template_executors.go +++ b/pkg/sdk/poc/generator/template_executors.go @@ -25,7 +25,7 @@ func GenerateInterface(writer io.Writer, def *Interface) { } func generateOptionsStruct(writer io.Writer, operation *Operation) { - printTo(writer, OptionsTemplate, operation) + printTo(writer, OperationStructTemplate, operation) for _, f := range operation.HelperStructs { if !slices.Contains(generatedStructs, f.KindNoPtr()) { @@ -84,12 +84,12 @@ func GenerateImplementation(writer io.Writer, def *Interface) { func GenerateUnitTests(writer io.Writer, def *Interface) { generatePackageDirective(writer) - printTo(writer, TestFuncTemplate, def) + printTo(writer, UnitTestsTemplate, def) } func GenerateValidations(writer io.Writer, def *Interface) { generatePackageDirective(writer) - printTo(writer, ValidationsImplTemplate, def) + printTo(writer, ValidationsTemplate, def) } func GenerateIntegrationTests(writer io.Writer, def *Interface) { diff --git a/pkg/sdk/poc/generator/templates.go b/pkg/sdk/poc/generator/templates.go index 3f3d2957a2..8d7d6b52b4 100644 --- a/pkg/sdk/poc/generator/templates.go +++ b/pkg/sdk/poc/generator/templates.go @@ -1,308 +1,88 @@ package generator -import "text/template" - -var PackageTemplate, _ = template.New("packageTemplate").Parse(` -package {{ . }} -`) - -var InterfaceTemplate, _ = template.New("interfaceTemplate"). - Funcs(template.FuncMap{ - "deref": func(p *DescriptionMappingKind) string { return string(*p) }, - }). - Parse(` -import "context" - -type {{ .Name }} interface { - {{- range .Operations }} - {{- if and (eq .Name "Show") .ShowMapping }} - {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) ([]{{ .ShowMapping.To.Name }}, error) - {{- else if eq .Name "ShowByID" }} - {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .ObjectInterface.NameSingular }}, error) - {{- else if and (eq .Name "Describe") .DescribeMapping }} - {{- if .DescribeKind }} - {{- if eq (deref .DescribeKind) "single_value" }} - {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .DescribeMapping.To.Name }}, error) - {{- else if eq (deref .DescribeKind) "slice" }} - {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) ([]{{ .DescribeMapping.To.Name }}, error) - {{- end }} - {{- end }} - {{- else }} - {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) error - {{- end -}} - {{ end }} -} -`) - -var OptionsTemplate, _ = template.New("optionsTemplate").Parse(` -// {{ .OptsField.KindNoPtr }} is based on {{ .Doc }}. -type {{ .OptsField.KindNoPtr }} struct { - {{- range .OptsField.Fields }} - {{ .Name }} {{ .Kind }} {{ .TagsPrintable }} - {{- end }} -} -`) - -// TODO: merge with template above? (requires moving Doc to field) -var StructTemplate, _ = template.New("structTemplate").Parse(` -type {{ .KindNoPtr }} struct { - {{- range .Fields }} - {{ .Name }} {{ .Kind }} {{ .TagsPrintable }} - {{- end }} -} -`) - -var DtoTemplate, _ = template.New("dtoTemplate").Parse(` -//go:generate go run ./dto-builder-generator/main.go - -var ( - {{- range .Operations }} - {{- if .OptsField }} - _ optionsProvider[{{ .OptsField.KindNoPtr }}] = new({{ .OptsField.DtoDecl }}) - {{- end }} - {{- end }} +import ( + _ "embed" + "text/template" ) -`) -var DtoDeclTemplate, _ = template.New("dtoTemplate").Parse(` -type {{ .DtoDecl }} struct { - {{- range .Fields }} - {{- if .ShouldBeInDto }} - {{ .Name }} {{ .DtoKind }} {{ if .Required }}// required{{ end }} - {{- end }} - {{- end }} -} -`) +var ( + //go:embed templates/package.tmpl + packageTemplateContent string + PackageTemplate, _ = template.New("packageTemplates").Parse(packageTemplateContent) -var ImplementationTemplate, _ = template.New("implementationTemplate"). - Funcs(template.FuncMap{ + //go:embed templates/interface.tmpl + interfaceTemplateContent string + InterfaceTemplate, _ = template.New("interfaceTemplate").Funcs(template.FuncMap{ "deref": func(p *DescriptionMappingKind) string { return string(*p) }, - }). - Parse(` -{{ define "MAPPING" -}} - &{{ .KindNoPtr }}{ - {{- range .Fields }} - {{- if .ShouldBeInDto }} - {{ if .IsStruct }}{{ else }}{{ .Name }}: r{{ .Path }},{{ end -}} - {{- end -}} - {{- end }} - } - {{- range .Fields }} - {{- if .ShouldBeInDto }} - {{- if .IsStruct }} - if r{{ .Path }} != nil { - {{- if not .IsSlice }} - opts{{ .Path }} = {{ template "MAPPING" . -}} - {{- else }} - s := make({{ .Kind }}, len(r{{ .Path }})) - for i, v := range r{{ .Path }} { - s[i] = {{ .KindNoSlice }}{ - {{- range .Fields }} - {{ .Name }}: v.{{ .Name }}, - {{- end }} - } - } - opts{{ .Path }} = s - {{ end -}} - } - {{- end -}} - {{ end -}} - {{ end }} -{{ end }} -{{ define "MAPPING_FUNC" }} - func (r {{ .From.Name }}) {{ .MappingFuncName }}() *{{ .To.KindNoPtr }} { - // TODO: Mapping - return &{{ .To.KindNoPtr }}{} - } -{{ end }} -import ( -"context" + }).Parse(interfaceTemplateContent) -"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" -) + //go:embed templates/operation_struct.tmpl + operationStructTemplateContent string + OperationStructTemplate, _ = template.New("optionsTemplate").Parse(operationStructTemplateContent) -{{ $impl := .NameLowerCased }} -var _ {{ .Name }} = (*{{ $impl }})(nil) + //go:embed templates/struct.tmpl + structTemplateContent string + StructTemplate, _ = template.New("structTemplate").Parse(structTemplateContent) -type {{ $impl }} struct { - client *Client -} -{{ range .Operations }} - {{ if and (eq .Name "Show") .ShowMapping }} - func (v *{{ $impl }}) Show(ctx context.Context, request *{{ .OptsField.DtoDecl }}) ([]{{ .ShowMapping.To.Name }}, error) { - opts := request.toOpts() - dbRows, err := validateAndQuery[{{ .ShowMapping.From.Name }}](v.client, ctx, opts) - if err != nil { - return nil, err - } - resultList := convertRows[{{ .ShowMapping.From.Name }}, {{ .ShowMapping.To.Name }}](dbRows) - return resultList, nil - } - {{ else if eq .Name "ShowByID" }} - func (v *{{ $impl }}) ShowByID(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .ObjectInterface.NameSingular }}, error) { - // TODO: adjust request if e.g. LIKE is supported for the resource - {{ $impl }}, err := v.Show(ctx, NewShow{{ .ObjectInterface.NameSingular }}Request()) - if err != nil { - return nil, err - } - return collections.FindOne({{ $impl }}, func(r {{ .ObjectInterface.NameSingular }}) bool { return r.Name == id.Name() }) - } - {{ else if and (eq .Name "Describe") .DescribeMapping }} - {{ if .DescribeKind }} - {{ if eq (deref .DescribeKind) "single_value" }} - func (v *{{ $impl }}) Describe(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .DescribeMapping.To.Name }}, error) { - opts := &{{ .OptsField.Name }}{ - name: id, - } - result, err := validateAndQueryOne[{{ .DescribeMapping.From.Name }}](v.client, ctx, opts) - if err != nil { - return nil, err - } - return result.convert(), nil - } - {{ else if eq (deref .DescribeKind) "slice" }} - func (v *{{ $impl }}) Describe(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) ([]{{ .DescribeMapping.To.Name }}, error) { - opts := &{{ .OptsField.Name }}{ - name: id, - } - rows, err := validateAndQuery[{{ .DescribeMapping.From.Name}}](v.client, ctx, opts) - if err != nil { - return nil, err - } - return convertRows[{{ .DescribeMapping.From.Name }}, {{ .DescribeMapping.To.Name }}](rows), nil - } - {{ end }} - {{ end }} - {{ else }} - func (v *{{ $impl }}) {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) error { - opts := request.toOpts() - return validateAndExec(v.client, ctx, opts) - } - {{ end }} -{{ end }} + //go:embed templates/dto_declarations.tmpl + dtoDeclarationsTemplateContent string + DtoTemplate, _ = template.New("dtoTemplate").Parse(dtoDeclarationsTemplateContent) -{{ range .Operations }} - {{- if .OptsField }} - func (r *{{ .OptsField.DtoDecl }}) toOpts() *{{ .OptsField.KindNoPtr }} { - opts := {{ template "MAPPING" .OptsField -}} - return opts - } - {{ if .ShowMapping }} - {{ template "MAPPING_FUNC" .ShowMapping }} - {{ end }} - {{ if .DescribeMapping }} - {{ template "MAPPING_FUNC" .DescribeMapping }} - {{ end }} - {{- end}} -{{ end }} -`) + //go:embed templates/dto_structs.tmpl + dtoStructsTemplateContent string + DtoDeclTemplate, _ = template.New("dtoTemplate").Parse(dtoStructsTemplateContent) -var TestFuncTemplate, _ = template.New("testFuncTemplate").Parse(` -{{ define "VALIDATION_TEST" }} - {{ $field := . }} - {{- range .Validations }} - t.Run("{{ .TodoComment $field }}", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsInvalidJoinedErrors(t, opts, {{ .ReturnedError $field }}) - }) - {{ end -}} -{{ end }} + //go:embed templates/integration_tests.tmpl + integrationTestTemplateContent string + IntegrationTestsTemplate, _ = template.New("integrationTestsTemplate").Parse(integrationTestTemplateContent) -{{ define "VALIDATIONS" }} - {{- template "VALIDATION_TEST" . -}} - {{ range .Fields }} - {{- if .HasAnyValidationInSubtree }} - {{- template "VALIDATIONS" . -}} - {{ end -}} - {{- end -}} -{{ end }} + //go:embed templates/implementation.tmpl + implementationTemplateContent string + ImplementationTemplate *template.Template -import "testing" + //go:embed templates/unit_tests.tmpl + unitTestTemplateContent string + UnitTestsTemplate *template.Template -{{ range .Operations }} - {{- if .OptsField }} - func Test{{ .ObjectInterface.Name }}_{{ .Name }}(t *testing.T) { - id := random{{ .ObjectInterface.IdentifierKind }}() + //go:embed templates/validations.tmpl + validationTemplateContent string + ValidationsTemplate *template.Template - // Minimal valid {{ .OptsField.KindNoPtr }} - defaultOpts := func() *{{ .OptsField.KindNoPtr }} { - return &{{ .OptsField.KindNoPtr }}{ - name: id, - } - } + //go:embed templates/sub_templates/to_opts_mapping.tmpl + toOptsMappingTemplateContent string - t.Run("validation: nil options", func(t *testing.T) { - var opts *{{ .OptsField.KindNoPtr }} = nil - assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) - }) + //go:embed templates/sub_templates/convert.tmpl + convertTemplateContent string - {{- template "VALIDATIONS" .OptsField }} + //go:embed templates/sub_templates/implementation_mappings.tmpl + implementationMappingsTemplateContent string - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) + //go:embed templates/sub_templates/implementation_functions.tmpl + implementationFunctionsTemplateContent string - t.Run("all options", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - } - {{- end }} -{{ end }} -`) + //go:embed templates/sub_templates/validation_test.tmpl + validationTestTemplateContent string -var ValidationsImplTemplate, _ = template.New("validationsImplTemplate").Parse(` -{{ define "VALIDATIONS" }} - {{- $field := . -}} - {{- range .Validations }} - if {{ .Condition $field }} { - errs = append(errs, {{ .ReturnedError $field }}) - } - {{- end -}} - {{- range .Fields }} - {{- if .HasAnyValidationInSubtree }} - if valueSet(opts{{ .Path }}) { - {{- template "VALIDATIONS" . }} - } - {{- end -}} - {{- end -}} -{{ end }} + //go:embed templates/sub_templates/validation_tests.tmpl + validationTestsTemplateContent string -var ( -{{- range .Operations }} - {{- if .OptsField }} - _ validatable = new({{ .OptsField.KindNoPtr }}) - {{- end }} -{{- end }} + //go:embed templates/sub_templates/validation_implementation.tmpl + validationImplementationTemplateContent string ) -{{ range .Operations }} - {{- if .OptsField }} - func (opts *{{ .OptsField.KindNoPtr }}) validate() error { - if opts == nil { - return ErrNilOptions - } - var errs []error - {{- template "VALIDATIONS" .OptsField }} - return JoinErrors(errs...) - } - {{- end }} -{{ end }} -`) - -var IntegrationTestsTemplate, _ = template.New("integrationTestsTemplate").Parse(` -import "testing" -func TestInt_{{ .Name }}(t *testing.T) { - // TODO: prepare common resources - - {{ range .Operations }} - t.Run("{{ .Name }}", func(t *testing.T) { - // TODO: fill me +func init() { + subTemplates := template.New("subTemplates").Funcs(template.FuncMap{ + "deref": func(p *DescriptionMappingKind) string { return string(*p) }, }) - {{ end -}} + subTemplates, _ = subTemplates.New("toOptsMapping").Parse(toOptsMappingTemplateContent) + subTemplates, _ = subTemplates.New("convert").Parse(convertTemplateContent) + subTemplates, _ = subTemplates.New("implementationMappings").Parse(implementationMappingsTemplateContent) + subTemplates, _ = subTemplates.New("implementationFunctions").Parse(implementationFunctionsTemplateContent) + subTemplates, _ = subTemplates.New("validationTest").Parse(validationTestTemplateContent) + subTemplates, _ = subTemplates.New("validationTests").Parse(validationTestsTemplateContent) + subTemplates, _ = subTemplates.New("validationImplementation").Parse(validationImplementationTemplateContent) + + ImplementationTemplate, _ = subTemplates.New("implementationTemplate").Parse(implementationTemplateContent) + UnitTestsTemplate, _ = subTemplates.New("unitTestsTemplate").Parse(unitTestTemplateContent) + ValidationsTemplate, _ = subTemplates.New("validationsTemplate").Parse(validationTemplateContent) } -`) diff --git a/pkg/sdk/poc/generator/templates/dto_declarations.tmpl b/pkg/sdk/poc/generator/templates/dto_declarations.tmpl new file mode 100644 index 0000000000..2374ad83fb --- /dev/null +++ b/pkg/sdk/poc/generator/templates/dto_declarations.tmpl @@ -0,0 +1,11 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +//go:generate go run ./dto-builder-generator/main.go + +var ( +{{- range .Operations }} + {{- if .OptsField }} + _ optionsProvider[{{ .OptsField.KindNoPtr }}] = new({{ .OptsField.DtoDecl }}) + {{- end }} +{{- end }} +) diff --git a/pkg/sdk/poc/generator/templates/dto_structs.tmpl b/pkg/sdk/poc/generator/templates/dto_structs.tmpl new file mode 100644 index 0000000000..518a833665 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/dto_structs.tmpl @@ -0,0 +1,10 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +type {{ .DtoDecl }} struct { +{{- range .Fields }} + {{- if .ShouldBeInDto }} + {{ .Name }} {{ .DtoKind }} {{ if .Required }}// required{{ end }} + {{- end }} +{{- end }} +} + diff --git a/pkg/sdk/poc/generator/templates/implementation.tmpl b/pkg/sdk/poc/generator/templates/implementation.tmpl new file mode 100644 index 0000000000..7381162f68 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/implementation.tmpl @@ -0,0 +1,18 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +{{ $impl := .NameLowerCased }} +var _ {{ .Name }} = (*{{ $impl }})(nil) + +type {{ $impl }} struct { +client *Client +} + +{{ template "implementationFunctions" . }} + +{{ template "implementationMappings" . }} diff --git a/pkg/sdk/poc/generator/templates/integration_tests.tmpl b/pkg/sdk/poc/generator/templates/integration_tests.tmpl new file mode 100644 index 0000000000..f814ba6f31 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/integration_tests.tmpl @@ -0,0 +1,13 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +import "testing" + +func TestInt_{{ .Name }}(t *testing.T) { +// TODO: prepare common resources + +{{ range .Operations }} + t.Run("{{ .Name }}", func(t *testing.T) { + // TODO: fill me + }) +{{ end -}} +} diff --git a/pkg/sdk/poc/generator/templates/interface.tmpl b/pkg/sdk/poc/generator/templates/interface.tmpl new file mode 100644 index 0000000000..213848ff34 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/interface.tmpl @@ -0,0 +1,23 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +import "context" + +type {{ .Name }} interface { + {{- range .Operations }} + {{- if and (eq .Name "Show") .ShowMapping }} + {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) ([]{{ .ShowMapping.To.Name }}, error) + {{- else if eq .Name "ShowByID" }} + {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .ObjectInterface.NameSingular }}, error) + {{- else if and (eq .Name "Describe") .DescribeMapping }} + {{- if .DescribeKind }} + {{- if eq (deref .DescribeKind) "single_value" }} + {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .DescribeMapping.To.Name }}, error) + {{- else if eq (deref .DescribeKind) "slice" }} + {{ .Name }}(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) ([]{{ .DescribeMapping.To.Name }}, error) + {{- end }} + {{- end }} + {{- else }} + {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) error + {{- end -}} + {{ end }} +} diff --git a/pkg/sdk/poc/generator/templates/operation_struct.tmpl b/pkg/sdk/poc/generator/templates/operation_struct.tmpl new file mode 100644 index 0000000000..4bb00b891a --- /dev/null +++ b/pkg/sdk/poc/generator/templates/operation_struct.tmpl @@ -0,0 +1,8 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Operation*/ -}} + +// {{ .OptsField.KindNoPtr }} is based on {{ .Doc }}. +type {{ .OptsField.KindNoPtr }} struct { +{{- range .OptsField.Fields }} + {{ .Name }} {{ .Kind }} {{ .TagsPrintable }} +{{- end }} +} diff --git a/pkg/sdk/poc/generator/templates/package.tmpl b/pkg/sdk/poc/generator/templates/package.tmpl new file mode 100644 index 0000000000..f097973bd3 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/package.tmpl @@ -0,0 +1,2 @@ + +package {{ . }} diff --git a/pkg/sdk/poc/generator/templates/struct.tmpl b/pkg/sdk/poc/generator/templates/struct.tmpl new file mode 100644 index 0000000000..b4ebac53c0 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/struct.tmpl @@ -0,0 +1,7 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +type {{ .KindNoPtr }} struct { +{{- range .Fields }} + {{ .Name }} {{ .Kind }} {{ .TagsPrintable }} +{{- end }} +} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/convert.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/convert.tmpl new file mode 100644 index 0000000000..36dcdd40c2 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/convert.tmpl @@ -0,0 +1,6 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Mapping*/ -}} + +func (r {{ .From.Name }}) {{ .MappingFuncName }}() *{{ .To.KindNoPtr }} { + // TODO: Mapping + return &{{ .To.KindNoPtr }}{} +} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/implementation_functions.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/implementation_functions.tmpl new file mode 100644 index 0000000000..575dd835d0 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/implementation_functions.tmpl @@ -0,0 +1,56 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +{{ $impl := .NameLowerCased }} +{{ range .Operations }} + {{ if and (eq .Name "Show") .ShowMapping }} + func (v *{{ $impl }}) Show(ctx context.Context, request *{{ .OptsField.DtoDecl }}) ([]{{ .ShowMapping.To.Name }}, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[{{ .ShowMapping.From.Name }}](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[{{ .ShowMapping.From.Name }}, {{ .ShowMapping.To.Name }}](dbRows) + return resultList, nil + } + {{ else if eq .Name "ShowByID" }} + func (v *{{ $impl }}) ShowByID(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .ObjectInterface.NameSingular }}, error) { + // TODO: adjust request if e.g. LIKE is supported for the resource + {{ $impl }}, err := v.Show(ctx, NewShow{{ .ObjectInterface.NameSingular }}Request()) + if err != nil { + return nil, err + } + return collections.FindOne({{ $impl }}, func(r {{ .ObjectInterface.NameSingular }}) bool { return r.Name == id.Name() }) + } + {{ else if and (eq .Name "Describe") .DescribeMapping }} + {{ if .DescribeKind }} + {{ if eq (deref .DescribeKind) "single_value" }} + func (v *{{ $impl }}) Describe(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) (*{{ .DescribeMapping.To.Name }}, error) { + opts := &{{ .OptsField.Name }}{ + name: id, + } + result, err := validateAndQueryOne[{{ .DescribeMapping.From.Name }}](v.client, ctx, opts) + if err != nil { + return nil, err + } + return result.convert(), nil + } + {{ else if eq (deref .DescribeKind) "slice" }} + func (v *{{ $impl }}) Describe(ctx context.Context, id {{ .ObjectInterface.IdentifierKind }}) ([]{{ .DescribeMapping.To.Name }}, error) { + opts := &{{ .OptsField.Name }}{ + name: id, + } + rows, err := validateAndQuery[{{ .DescribeMapping.From.Name}}](v.client, ctx, opts) + if err != nil { + return nil, err + } + return convertRows[{{ .DescribeMapping.From.Name }}, {{ .DescribeMapping.To.Name }}](rows), nil + } + {{ end }} + {{ end }} + {{ else }} + func (v *{{ $impl }}) {{ .Name }}(ctx context.Context, request *{{ .OptsField.DtoDecl }}) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) + } + {{ end }} +{{ end }} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/implementation_mappings.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/implementation_mappings.tmpl new file mode 100644 index 0000000000..a52370e8a5 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/implementation_mappings.tmpl @@ -0,0 +1,18 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +{{ range .Operations }} + {{- if .OptsField }} + func (r *{{ .OptsField.DtoDecl }}) toOpts() *{{ .OptsField.KindNoPtr }} { + opts := &{{ template "toOptsMapping" .OptsField -}} + return opts + } + + {{ if .ShowMapping }} + {{ template "convert" .ShowMapping }} + {{ end }} + + {{ if .DescribeMapping }} + {{ template "convert" .DescribeMapping }} + {{ end }} + {{- end}} +{{ end }} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl new file mode 100644 index 0000000000..d304292cc5 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl @@ -0,0 +1,36 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +{{ .KindNoPtr }}{ +{{- range .Fields }} + {{- if .ShouldBeInDto }} + {{ if .IsStruct }}{{ else }}{{ .Name }}: r{{ .Path }},{{ end -}} + {{- end -}} +{{- end }} +} +{{- range .Fields }} + {{- if .ShouldBeInDto }} + {{- if .IsStruct }} + {{ if or .IsPointer .IsSlice }} + if r{{ .Path }} != nil { + {{ end }} + + {{- if not .IsSlice }} + opts{{ .Path }} = {{ template "toOptsMapping" . -}}{{/* Recursive call */}} + {{- else }} + s := make({{ .Kind }}, len(r{{ .Path }})) + for i, v := range r{{ .Path }} { + s[i] = {{ .KindNoSlice }}{ + {{- range .Fields }} + {{ .Name }}: v.{{ .Name }}, + {{- end }} + } + } + opts{{ .Path }} = s + {{ end -}} + + {{ if or .IsPointer .IsSlice }} + } + {{ end }} + {{- end -}} + {{ end -}} +{{ end }} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/validation_implementation.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/validation_implementation.tmpl new file mode 100644 index 0000000000..c5a71acd61 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/validation_implementation.tmpl @@ -0,0 +1,15 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +{{- $field := . -}} +{{- range .Validations }} + if {{ .Condition $field }} { + errs = append(errs, {{ .ReturnedError $field }}) + } +{{- end -}} +{{- range .Fields }} + {{- if .HasAnyValidationInSubtree }} + if valueSet(opts{{ .Path }}) { + {{- template "validationImplementation" . }}{{/* Recursive call */}} + } + {{- end -}} +{{- end -}} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/validation_test.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/validation_test.tmpl new file mode 100644 index 0000000000..426f6e1ace --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/validation_test.tmpl @@ -0,0 +1,10 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +{{ $field := . }} +{{- range .Validations }} + t.Run("{{ .TodoComment $field }}", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, {{ .ReturnedError $field }}) + }) +{{ end -}} diff --git a/pkg/sdk/poc/generator/templates/sub_templates/validation_tests.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/validation_tests.tmpl new file mode 100644 index 0000000000..9270ebac39 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/sub_templates/validation_tests.tmpl @@ -0,0 +1,8 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Field*/ -}} + +{{- template "validationTest" . -}} +{{ range .Fields }} + {{- if .HasAnyValidationInSubtree }} + {{- template "validationTests" . -}}{{/* Recursive call */}} + {{ end -}} +{{- end -}} diff --git a/pkg/sdk/poc/generator/templates/unit_tests.tmpl b/pkg/sdk/poc/generator/templates/unit_tests.tmpl new file mode 100644 index 0000000000..329cfda8ca --- /dev/null +++ b/pkg/sdk/poc/generator/templates/unit_tests.tmpl @@ -0,0 +1,41 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +import "testing" + +{{ range .Operations }} + {{- if .OptsField }} + func Test{{ .ObjectInterface.Name }}_{{ .Name }}(t *testing.T) { + {{ if not (eq .Name "Show") }} + id := random{{ .ObjectInterface.IdentifierKind }}() + {{ end -}} + + // Minimal valid {{ .OptsField.KindNoPtr }} + defaultOpts := func() *{{ .OptsField.KindNoPtr }} { + return &{{ .OptsField.KindNoPtr }}{ + {{ if not (eq .Name "Show") }} + name: id, + {{ end -}} + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *{{ .OptsField.KindNoPtr }} = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + {{- template "validationTests" .OptsField }} + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + } + {{- end }} +{{ end }} diff --git a/pkg/sdk/poc/generator/templates/validations.tmpl b/pkg/sdk/poc/generator/templates/validations.tmpl new file mode 100644 index 0000000000..09c9f43af0 --- /dev/null +++ b/pkg/sdk/poc/generator/templates/validations.tmpl @@ -0,0 +1,22 @@ +{{- /*gotype: github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator.Interface*/ -}} + +var ( +{{- range .Operations }} + {{- if .OptsField }} + _ validatable = new({{ .OptsField.KindNoPtr }}) + {{- end }} +{{- end }} +) + +{{ range .Operations }} + {{- if .OptsField }} + func (opts *{{ .OptsField.KindNoPtr }}) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + {{- template "validationImplementation" .OptsField }} + return JoinErrors(errs...) + } + {{- end }} +{{ end }}