Template overrides in new config style not possible?
Firstly, thanks for the great work.
While converting our configs to the new style I found that the config generator adds the actual templates as plain text under output-options.user-templates instead of referencing the template location provided with --templates. Having to update every config file after the template changes defeats the purpose of having a template.
Is there currently an option in the new config style that works similar to the old --templates flag? If not, is it possible to add an alternative to the new config that behaves like the old flag?
The approach I took was to create a local application which runs codegen.Generate.
This is rough but it'll give you an idea:
package main
import (
"context"
"embed"
"flag"
"io/fs"
"log"
"os"
"path"
"strings"
"text/template"
"github.com/deepmap/oapi-codegen/pkg/codegen"
"github.com/deepmap/oapi-codegen/pkg/util"
"github.com/getkin/kin-openapi/openapi3"
"gopkg.in/yaml.v3"
)
type configuration struct {
codegen.Configuration `yaml:",inline"`
// OutputFile is the filename to output.
OutputFile string `yaml:"output,omitempty"`
}
//go:embed templates
var templates embed.FS
// add template functions here
var templateFunctions template.FuncMap = template.FuncMap{}
func main() {
log.SetFlags(0)
var cfgpath string
flag.StringVar(&cfgpath, "config", "", "path to config file")
flag.Parse()
if cfgpath == "" {
log.Fatal("--config is required")
}
if flag.NArg() < 1 {
log.Fatal("Please specify a path to an OpenAPI 3.0 spec file")
}
// loading specification
input := flag.Arg(0)
spec, err := util.LoadSwagger(input)
if err != nil {
log.Fatalf("error loading openapi specification: %v", err)
}
err = spec.Validate(context.Background())
if err != nil {
log.Fatalf("error validating openapi specification: %v", err)
}
// loading configuration
cfgdata, err := os.ReadFile(cfgpath)
if err != nil {
log.Fatalf("error reading config file: %s", err)
}
var cfg configuration
err = yaml.Unmarshal(cfgdata, &cfg)
if err != nil {
log.Fatalf("error unmarshaling config %v", err)
}
// generating output
output, err := generate(spec, cfg.Configuration, templates)
if err != nil {
log.Fatalf("error generating code: %v", err)
}
// writing output to file
outFile, err := os.Create(cfg.OutputFile)
if err != nil {
log.Fatalf("error creating output file: %v", err)
}
_, err = outFile.Write([]byte(output))
if err != nil {
log.Fatalf("error writing output file: %v", err)
}
outFile.Close()
}
func generate(spec *openapi3.T, config codegen.Configuration, templates embed.FS) (string, error) {
var err error
config, err = addTemplateOverrides(config, templates)
if err != nil {
return "", err
}
// adding local template functions
for k, v := range templateFunctions {
codegen.TemplateFunctions[k] = v
}
return codegen.Generate(spec, config)
}
func addTemplateOverrides(config codegen.Configuration, templates embed.FS) (codegen.Configuration, error) {
overrides := config.OutputOptions.UserTemplates
if overrides == nil {
overrides = make(map[string]string)
}
err := fs.WalkDir(templates, ".", func(p string, d fs.DirEntry, err error) error {
if !d.IsDir() {
if err != nil {
return err
}
f, err := templates.ReadFile(p)
if err != nil {
return err
}
// using .gtpl for light syntax highlighting
name := strings.TrimSuffix(p, path.Ext(p)) + ".tmpl"
name = strings.Join(strings.Split(name, "/")[1:], "/")
overrides[name] = string(f)
}
return nil
})
config.OutputOptions.UserTemplates = overrides
return config, err
}
To generate with it, you'd do something like (assuming you named it generate-go)
//go:generate go run github.com/{github-user}/{project-id}/cmd/generate-go --config=api.server.yaml ../../spec/api.yaml
@gerardsn The user-templates I define in the YAML configuration file also aren't getting applied for me; are they working for you? What solution did you wind up using?
@dlek
The
user-templatesI define in the YAML configuration file also aren't getting applied for me; are they working for you?
Yes, they are working for me. I generated the config files using the "old setup" and the --output-config flag.
What solution did you wind up using?
We have had no changes to our templates, so I'm still using the config files with inline template definitions for now.
@gerardsn Thanks for your reply. I was doing the same--but the templates I was using involve a subdirectory in the original (oapi-codegen) template location, and so I needed the specify the templates accordingly:
output-options:
user-templates:
echo/echo-interface.tmpl: |
// ServerInterface represents all server handlers.
type ServerInterface interface {
[...]
(I also used different YAML syntax for multiline strings to make the templates more readable and editable in the configuration.)
thanks @dlek your code snippet helps
As noted in https://github.com/deepmap/oapi-codegen/pull/653#issuecomment-1710457277 it's possible to do this via https://github.com/deepmap/oapi-codegen/pull/968 - closing this issue (but please let me know if I've missed something!)