oapi-codegen
oapi-codegen copied to clipboard
Adds `user-templates-dir` to `OutputOptions`
This addresses #607 and is an alternative or companion to #653.
Adds a new configuration parameter, user-templates-dir, on OutputOptions which allows for assigning a directory path to parse user-template overrides. It re-uses the existing logic found in loadTemplateOverrides but is replicated in the codegen package.
user-templates takes precedence over user-templates-dir.
Setting up a local command that calls codegen.Generate seems like the better path than adding onto the API. Closing this PR.
@chanced Could you elaborate on this? What do you mean by local command?
@gmulders
The way I ended up addressing this was to create a wrapper application which loaded templates & template functions and then called 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-api)
//go:generate go run github.com/{github-user}/{project-id}/cmd/generate-go-api --config=api.server.yaml ../../spec/api.yaml
@w32blaster I saw your comment on the other pull request regarding templates. You may be interested in the code above.
maintainers, I'm re-opening this incase you find merit in it. Please feel free to close.
Awesome! Thank you @chanced !