-
Notifications
You must be signed in to change notification settings - Fork 427
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Improve generator template organization (#2820)
Changes done: - Templates were moved to files, so that - We get proper highlighting from IDE - We get help with model type and fields or methods inside it + ability to go to definition and more (https://blog.jetbrains.com/go/2018/12/14/go-templates-made-easy/) - Reorganized into smaller chunks, so it should be easier to navigate (main templates are in the `templates` directory, and helper templates used inside the main ones are kept inside the `subtemplates` directory). - In the `templates.go` now all of the templates are loaded by embed, except the ones that use other templates which have to be initiated in `func init()`. - All of the testing was made on the `managed account` resource and it shouldn't collide with #2816 - Also, there were made small adjustments like - Checking for operation name inside unit tests so we won't make invalid Show test - Mapping adjusted to the changes made in #2816
- Loading branch information
1 parent
05b7eee
commit 5035e2f
Showing
19 changed files
with
370 additions
and
286 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
`) |
Oops, something went wrong.