From 8b1ce929962bcb86b79e0083948f57801177ef71 Mon Sep 17 00:00:00 2001 From: Casey Marshall Date: Thu, 28 Oct 2021 10:35:52 -0500 Subject: [PATCH] feat: embed compiled OpenAPI specs in Go applications. --- internal/compiler/compiler.go | 46 +++++++++++++++++++++++++++++++++++ testdata/embed_test.go | 20 +++++++++++++++ testdata/output/embed.go | 32 ++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 testdata/embed_test.go create mode 100644 testdata/output/embed.go diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 71c49673..e81cac5e 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -3,6 +3,7 @@ package compiler import ( "context" "fmt" + "html/template" "io/fs" "io/ioutil" "log" @@ -268,6 +269,7 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error { return fmt.Errorf("failed to create output directory: %w", err) } log.Printf("compiling API %s to output versions", apiName) + var versionSpecFiles []string for rcIndex, rc := range api.resources { specVersions, err := vervet.LoadSpecVersionsFileset(rc.matchedFiles) if err != nil { @@ -312,6 +314,11 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error { return buildErr(err) } jsonSpecPath := versionDir + "/spec.json" + jsonEmbedPath, err := filepath.Rel(api.output.path, jsonSpecPath) + if err != nil { + return buildErr(err) + } + versionSpecFiles = append(versionSpecFiles, jsonEmbedPath) err = ioutil.WriteFile(jsonSpecPath, jsonBuf, 0644) if err != nil { return buildErr(err) @@ -326,6 +333,11 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error { return buildErr(err) } yamlSpecPath := versionDir + "/spec.yaml" + yamlEmbedPath, err := filepath.Rel(api.output.path, yamlSpecPath) + if err != nil { + return buildErr(err) + } + versionSpecFiles = append(versionSpecFiles, yamlEmbedPath) err = ioutil.WriteFile(yamlSpecPath, yamlBuf, 0644) if err != nil { return buildErr(err) @@ -334,9 +346,43 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error { } } } + err = c.writeEmbedGo(filepath.Base(api.output.path), api, versionSpecFiles) + if err != nil { + return fmt.Errorf("failed to create embed.go: %w", err) + } return nil } +func (c *Compiler) writeEmbedGo(pkgName string, a *api, versionSpecFiles []string) error { + f, err := os.Create(filepath.Join(a.output.path, "embed.go")) + if err != nil { + return err + } + defer f.Close() + return embedGoTmpl.Execute(f, struct { + Package string + API *api + VersionSpecFiles []string + }{ + Package: pkgName, + API: a, + VersionSpecFiles: versionSpecFiles, + }) +} + +var embedGoTmpl = template.Must(template.New("embed.go").Parse(` +package {{ .Package }} + +import "embed" + +// Embed compiled OpenAPI specs in Go projects. + +{{ range .VersionSpecFiles -}} +//go:embed {{ . }} +{{ end -}} +var Versions embed.FS +`)) + // BuildAll builds all APIs in the project. func (c *Compiler) BuildAll(ctx context.Context) error { return c.apisEach(ctx, c.Build) diff --git a/testdata/embed_test.go b/testdata/embed_test.go new file mode 100644 index 00000000..0b55f62b --- /dev/null +++ b/testdata/embed_test.go @@ -0,0 +1,20 @@ +package testdata + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "github.com/getkin/kin-openapi/openapi3" + + "github.com/snyk/vervet/testdata/output" +) + +func TestEmbedding(t *testing.T) { + c := qt.New(t) + + specYAML, err := output.Versions.ReadFile("2021-06-13~experimental/spec.yaml") + c.Assert(err, qt.IsNil) + l := openapi3.NewLoader() + _, err = l.LoadFromData(specYAML) + c.Assert(err, qt.IsNil) +} diff --git a/testdata/output/embed.go b/testdata/output/embed.go new file mode 100644 index 00000000..ea4a6896 --- /dev/null +++ b/testdata/output/embed.go @@ -0,0 +1,32 @@ + +package output + +import "embed" + +// Embed compiled OpenAPI specs in Go projects. + +//go:embed 2021-06-01~experimental/spec.json +//go:embed 2021-06-01~experimental/spec.yaml +//go:embed 2021-06-01~beta/spec.json +//go:embed 2021-06-01~beta/spec.yaml +//go:embed 2021-06-01/spec.json +//go:embed 2021-06-01/spec.yaml +//go:embed 2021-06-04~experimental/spec.json +//go:embed 2021-06-04~experimental/spec.yaml +//go:embed 2021-06-04~beta/spec.json +//go:embed 2021-06-04~beta/spec.yaml +//go:embed 2021-06-04/spec.json +//go:embed 2021-06-04/spec.yaml +//go:embed 2021-06-07~experimental/spec.json +//go:embed 2021-06-07~experimental/spec.yaml +//go:embed 2021-06-07~beta/spec.json +//go:embed 2021-06-07~beta/spec.yaml +//go:embed 2021-06-07/spec.json +//go:embed 2021-06-07/spec.yaml +//go:embed 2021-06-13~experimental/spec.json +//go:embed 2021-06-13~experimental/spec.yaml +//go:embed 2021-06-13~beta/spec.json +//go:embed 2021-06-13~beta/spec.yaml +//go:embed 2021-06-13/spec.json +//go:embed 2021-06-13/spec.yaml +var Versions embed.FS