From bf33d492ded848e6b19dc91de4d612c6a9f07d85 Mon Sep 17 00:00:00 2001 From: Jack Lee Date: Fri, 1 Jul 2022 18:29:26 -0400 Subject: [PATCH 1/2] Add template overrides from files in new config --- pkg/codegen/codegen.go | 33 +++++++++++++++++++++++++++++++++ pkg/codegen/configuration.go | 12 +++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index 50866917e8..1c4515edfe 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -18,8 +18,11 @@ import ( "bufio" "bytes" "embed" + "errors" "fmt" "io/fs" + "os" + "path" "runtime/debug" "sort" "strings" @@ -130,6 +133,36 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { // Override built-in templates with user-provided versions for _, tpl := range t.Templates() { + // Check for template in provided template directory + if dir := opts.OutputOptions.UserTemplatesDir; dir != "" { + fp := path.Join(dir, tpl.Name()) + _, err := os.Stat(fp) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return "", fmt.Errorf("error accessing user-provided template %q: %w", fp, err) + } + if err == nil { + utpl := t.New(tpl.Name()) + data, err := os.ReadFile(fp) + if err != nil { + return "", fmt.Errorf("error reading user-provided template %q: %w", fp, err) + } + if _, err := utpl.Parse(string(data)); err != nil { + return "", fmt.Errorf("error parsing user-provided template %q: %w", fp, err) + } + } + } + // Check for template in a provided file path + if fp, ok := opts.OutputOptions.UserTemplateFiles[tpl.Name()]; ok { + utpl := t.New(tpl.Name()) + data, err := os.ReadFile(fp) + if err != nil { + return "", fmt.Errorf("error reading user-provided template %q: %w", fp, err) + } + if _, err := utpl.Parse(string(data)); err != nil { + return "", fmt.Errorf("error parsing user-provided template %q: %w", fp, err) + } + } + // Check for template provided inline in the configuration if _, ok := opts.OutputOptions.UserTemplates[tpl.Name()]; ok { utpl := t.New(tpl.Name()) if _, err := utpl.Parse(opts.OutputOptions.UserTemplates[tpl.Name()]); err != nil { diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index 1400c3c740..5dd0effd28 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -73,11 +73,13 @@ type CompatibilityOptions struct { // OutputOptions are used to modify the output code in some way. type OutputOptions struct { - SkipFmt bool `yaml:"skip-fmt,omitempty"` // Whether to skip go imports on the generated code - SkipPrune bool `yaml:"skip-prune,omitempty"` // Whether to skip pruning unused components on the generated code - IncludeTags []string `yaml:"include-tags,omitempty"` // Only include operations that have one of these tags. Ignored when empty. - ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty. - UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files + SkipFmt bool `yaml:"skip-fmt,omitempty"` // Whether to skip go imports on the generated code + SkipPrune bool `yaml:"skip-prune,omitempty"` // Whether to skip pruning unused components on the generated code + IncludeTags []string `yaml:"include-tags,omitempty"` // Only include operations that have one of these tags. Ignored when empty. + ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty. + UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files + UserTemplateFiles map[string]string `yaml:"user-template-files,omitempty"` // Same as UserTemplates, with filenames as values instead + UserTemplatesDir string `yaml:"user-templates-dir,omitempty"` ExcludeSchemas []string `yaml:"exclude-schemas,omitempty"` // Exclude from generation schemas with given names. Ignored when empty. ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types From b031c5bbb460151914cefde703ab36dbe894deb4 Mon Sep 17 00:00:00 2001 From: Jack Lee Date: Wed, 23 Nov 2022 19:53:29 -0500 Subject: [PATCH 2/2] Add tests for template overrides --- pkg/codegen/codegen_test.go | 87 ++++++++++++++------- pkg/codegen/testdata/chi-interface.tmpl | 1 + pkg/codegen/testdata/chi/chi-interface.tmpl | 1 + 3 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 pkg/codegen/testdata/chi-interface.tmpl create mode 100644 pkg/codegen/testdata/chi/chi-interface.tmpl diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go index 4a85eb1e92..a4b4dfe05e 100644 --- a/pkg/codegen/codegen_test.go +++ b/pkg/codegen/codegen_test.go @@ -3,18 +3,20 @@ package codegen import ( "bytes" _ "embed" + "fmt" "go/format" "io" "net/http" "testing" - examplePetstoreClient "github.com/deepmap/oapi-codegen/examples/petstore-expanded" - examplePetstore "github.com/deepmap/oapi-codegen/examples/petstore-expanded/echo/api" - "github.com/deepmap/oapi-codegen/pkg/util" "github.com/getkin/kin-openapi/openapi3" "github.com/golangci/lint-1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + examplePetstoreClient "github.com/deepmap/oapi-codegen/examples/petstore-expanded" + examplePetstore "github.com/deepmap/oapi-codegen/examples/petstore-expanded/echo/api" + "github.com/deepmap/oapi-codegen/pkg/util" ) const ( @@ -76,39 +78,68 @@ func TestExamplePetStoreCodeGeneration(t *testing.T) { } func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) { - - userTemplates := map[string]string{"typedef.tmpl": "//blah"} - // Input vars for code generation: packageName := "api" opts := Configuration{ PackageName: packageName, Generate: GenerateOptions{ - Models: true, - }, - OutputOptions: OutputOptions{ - UserTemplates: userTemplates, + ChiServer: true, + Models: true, }, } - // Get a spec from the example PetStore definition: - swagger, err := examplePetstore.GetSwagger() - assert.NoError(t, err) - - // Run our code generation: - code, err := Generate(swagger, opts) - assert.NoError(t, err) - assert.NotEmpty(t, code) - - // Check that we have valid (formattable) code: - _, err = format.Source([]byte(code)) - assert.NoError(t, err) - - // Check that we have a package: - assert.Contains(t, code, "package api") - - // Check that the built-in template has been overriden - assert.Contains(t, code, "//blah") + for set := 0; set < 8; set++ { + var ( + oo OutputOptions + useDirectoryOverride = set&4 != 0 + useFileOverride = set&2 != 0 + useInlineOverride = set&1 != 0 + ) + if useDirectoryOverride { + oo.UserTemplatesDir = "testdata" + } + if useFileOverride { + oo.UserTemplateFiles = map[string]string{"chi/chi-interface.tmpl": "testdata/chi-interface.tmpl"} + } + if useInlineOverride { + oo.UserTemplates = map[string]string{"chi/chi-interface.tmpl": "// inline override"} + } + opts.OutputOptions = oo + + testName := fmt.Sprintf( + "override: directory=%v, file=%v, inline=%v", + useDirectoryOverride, + useFileOverride, + useInlineOverride, + ) + t.Run(testName, func(t *testing.T) { + // Get a spec from the example PetStore definition: + swagger, err := examplePetstore.GetSwagger() + assert.NoError(t, err) + + // Run our code generation: + code, err := Generate(swagger, opts) + assert.NoError(t, err) + assert.NotEmpty(t, code) + + // Check that we have valid (formattable) code: + _, err = format.Source([]byte(code)) + assert.NoError(t, err) + + // Check that we have a package: + assert.Contains(t, code, "package api") + + // Check that the generated code has the appropriate template override applied + switch { + case useInlineOverride: + assert.Contains(t, code, "// inline override") + case useFileOverride: + assert.Contains(t, code, "// file override") + case useDirectoryOverride: + assert.Contains(t, code, "// directory override") + } + }) + } } func TestExamplePetStoreParseFunction(t *testing.T) { diff --git a/pkg/codegen/testdata/chi-interface.tmpl b/pkg/codegen/testdata/chi-interface.tmpl new file mode 100644 index 0000000000..4a0603e5b6 --- /dev/null +++ b/pkg/codegen/testdata/chi-interface.tmpl @@ -0,0 +1 @@ +// file override \ No newline at end of file diff --git a/pkg/codegen/testdata/chi/chi-interface.tmpl b/pkg/codegen/testdata/chi/chi-interface.tmpl new file mode 100644 index 0000000000..0bec22df35 --- /dev/null +++ b/pkg/codegen/testdata/chi/chi-interface.tmpl @@ -0,0 +1 @@ +// directory override \ No newline at end of file