diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go index 3ccce8d93b..bc0646845d 100644 --- a/examples/petstore-expanded/chi/api/petstore.gen.go +++ b/examples/petstore-expanded/chi/api/petstore.gen.go @@ -9,6 +9,8 @@ import ( "encoding/base64" "fmt" "net/http" + "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -281,9 +283,9 @@ var swaggerSpec = []string{ "u/vdPwIAAP//v4qmX1cSAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -298,9 +300,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return + } + swagger, err = loader.LoadSwaggerFromData(specData) + if err != nil { + return } - return swagger, nil + return } diff --git a/examples/petstore-expanded/echo/api/petstore-server.gen.go b/examples/petstore-expanded/echo/api/petstore-server.gen.go index a38f8a9e55..d9245d656a 100644 --- a/examples/petstore-expanded/echo/api/petstore-server.gen.go +++ b/examples/petstore-expanded/echo/api/petstore-server.gen.go @@ -9,6 +9,8 @@ import ( "encoding/base64" "fmt" "net/http" + "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -171,9 +173,9 @@ var swaggerSpec = []string{ "u/vdPwIAAP//v4qmX1cSAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -188,9 +190,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/client/client.gen.go b/internal/test/client/client.gen.go index de3d25720b..399698b25f 100644 --- a/internal/test/client/client.gen.go +++ b/internal/test/client/client.gen.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -1043,9 +1044,9 @@ var swaggerSpec = []string{ "3zLVjhd6b4bhVwAAAP//2pHiCAkHAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -1060,9 +1061,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go index 432db0b21b..94beba2ed1 100644 --- a/internal/test/components/components.gen.go +++ b/internal/test/components/components.gen.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -1396,9 +1397,9 @@ var swaggerSpec = []string{ "fwAAAP//Xv36ip0PAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -1413,9 +1414,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/externalref/externalref.cfg.yaml b/internal/test/externalref/externalref.cfg.yaml index 92d36a544b..82fe5967dd 100644 --- a/internal/test/externalref/externalref.cfg.yaml +++ b/internal/test/externalref/externalref.cfg.yaml @@ -4,6 +4,7 @@ package: externalref generate: - types - skip-prune + - spec import-mapping: ./packageA/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageA ./packageB/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageB diff --git a/internal/test/externalref/externalref.gen.go b/internal/test/externalref/externalref.gen.go index 39eaf372b0..0f63a4f22a 100644 --- a/internal/test/externalref/externalref.gen.go +++ b/internal/test/externalref/externalref.gen.go @@ -4,8 +4,17 @@ package externalref import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/url" + "path" + "strings" + externalRef0 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageA" externalRef1 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageB" + "github.com/getkin/kin-openapi/openapi3" ) // Container defines model for Container. @@ -13,3 +22,97 @@ type Container struct { ObjectA *externalRef0.ObjectA `json:"object_a,omitempty"` ObjectB *externalRef1.ObjectB `json:"object_b,omitempty"` } + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/4zPQcrCMBAF4Lu8/1+GpOAuu9YDeASZhqmNtsmQBEFK7i4pihsXZvUeYb5hNri4Sgwc", + "SobdkN3MK+3xGEMhHzi1IikKp+J5/4rjlV05U8v/iSdYaCPkbnTh3mRhpx+0Ln/mg5uXbE77bI+q3sz4", + "jRl+YgbU9hR8mGJjii8LwwIKd07Zx9BK2yUcSDwsDrrTHRSEytyuqfUZAAD//0P8tE0FAQAA", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + pathPrefix := path.Dir(pathToFile) + + for rawPath, rawFunc := range externalRef0.PathToRawSpec(path.Join(pathPrefix, "./packageA/spec.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + for rawPath, rawFunc := range externalRef1.PathToRawSpec(path.Join(pathPrefix, "./packageB/spec.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/externalref/imports_test.go b/internal/test/externalref/imports_test.go index 284964ae1b..0ca9e39371 100644 --- a/internal/test/externalref/imports_test.go +++ b/internal/test/externalref/imports_test.go @@ -3,6 +3,8 @@ package externalref import ( "testing" + "github.com/stretchr/testify/require" + "github.com/deepmap/oapi-codegen/internal/test/externalref/packageA" "github.com/deepmap/oapi-codegen/internal/test/externalref/packageB" ) @@ -14,3 +16,14 @@ func TestParameters(t *testing.T) { ObjectB: b, } } + +func TestGetSwagger(t *testing.T) { + _, err := packageB.GetSwagger() + require.Nil(t, err) + + _, err = packageA.GetSwagger() + require.Nil(t, err) + + _, err = GetSwagger() + require.Nil(t, err) +} \ No newline at end of file diff --git a/internal/test/externalref/packageA/doc.go b/internal/test/externalref/packageA/doc.go index 410765f2ab..6d851c8a84 100644 --- a/internal/test/externalref/packageA/doc.go +++ b/internal/test/externalref/packageA/doc.go @@ -1,3 +1,3 @@ package packageA -//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=packageA -o externalref.gen.go --import-mapping=../packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml +//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune,spec --package=packageA -o externalref.gen.go --import-mapping=../packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml diff --git a/internal/test/externalref/packageA/externalref.gen.go b/internal/test/externalref/packageA/externalref.gen.go index a3d1e0bf99..7062f88858 100644 --- a/internal/test/externalref/packageA/externalref.gen.go +++ b/internal/test/externalref/packageA/externalref.gen.go @@ -4,7 +4,16 @@ package packageA import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/url" + "path" + "strings" + externalRef0 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageB" + "github.com/getkin/kin-openapi/openapi3" ) // ObjectA defines model for ObjectA. @@ -12,3 +21,91 @@ type ObjectA struct { Name *string `json:"name,omitempty"` ObjectB *externalRef0.ObjectB `json:"object_b,omitempty"` } + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/0SNQarDMAwF7/L+X5p4711zgR6hKEZJ3MaysN1FCLl7sVOoNhoYHnPAp6hJWGqBO1D8", + "ypE63qcn+3prqDkp5xq4C6HI7dddGQ6l5iALToPUF4+pyf/MMxyGwSr5Fy082qLsh53i9md/Ufst2is3", + "4mxnEGROcPLeNoOkLKQBDjBQqmu5zPkJAAD//0utOZO+AAAA", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + pathPrefix := path.Dir(pathToFile) + + for rawPath, rawFunc := range externalRef0.PathToRawSpec(path.Join(pathPrefix, "../packageB/spec.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/externalref/packageB/doc.go b/internal/test/externalref/packageB/doc.go index 4d28c8c007..29c31854c5 100644 --- a/internal/test/externalref/packageB/doc.go +++ b/internal/test/externalref/packageB/doc.go @@ -1,3 +1,3 @@ package packageB -//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=packageB -o externalref.gen.go spec.yaml +//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune,spec --package=packageB -o externalref.gen.go spec.yaml diff --git a/internal/test/externalref/packageB/externalref.gen.go b/internal/test/externalref/packageB/externalref.gen.go index 5736e7cceb..327c238c28 100644 --- a/internal/test/externalref/packageB/externalref.gen.go +++ b/internal/test/externalref/packageB/externalref.gen.go @@ -3,7 +3,98 @@ // Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT. package packageB +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" +) + // ObjectB defines model for ObjectB. type ObjectB struct { Name *string `json:"name,omitempty"` } + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/yTJwQ0CMQxE0V7mnApypAFqCNHAGm1sKzYHtNreUZa5zJfegW7DTakZqAeibxztyvvj", + "zZ63lT7NOVN4gbbB9fl1oiJyir5wrhWIPg1VP/teYE5tLqhAgbfc4i/nLwAA//8neaWPdgAAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/issues/issue-312/issue.gen.go b/internal/test/issues/issue-312/issue.gen.go index 2eed224666..2d834b1df4 100644 --- a/internal/test/issues/issue-312/issue.gen.go +++ b/internal/test/issues/issue-312/issue.gen.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -512,9 +513,9 @@ var swaggerSpec = []string{ "lN/KGdrR4Z+ALWOncMJcLsF1fCml9DcAAP//Z6kfG5EFAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -529,9 +530,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/issues/issue-52/issue.gen.go b/internal/test/issues/issue-52/issue.gen.go index 891f77939c..4921ab5731 100644 --- a/internal/test/issues/issue-52/issue.gen.go +++ b/internal/test/issues/issue-52/issue.gen.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -376,9 +377,9 @@ var swaggerSpec = []string{ "XWfxcNim33I9LkfJyNBpNI/3gk7T9BEAAP//t/QkwqkCAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -393,9 +394,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/issues/issue-grab_import_names/issue.gen.go b/internal/test/issues/issue-grab_import_names/issue.gen.go index d3824bb9b7..dbef99d217 100644 --- a/internal/test/issues/issue-grab_import_names/issue.gen.go +++ b/internal/test/issues/issue-grab_import_names/issue.gen.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -371,9 +372,9 @@ var swaggerSpec = []string{ "e8xb3DcLXNr5CgAA//+IR/EuPgIAAA==", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -388,9 +389,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/issues/issue-illegal_enum_names/issue.gen.go b/internal/test/issues/issue-illegal_enum_names/issue.gen.go index c085e795fd..dd30929725 100644 --- a/internal/test/issues/issue-illegal_enum_names/issue.gen.go +++ b/internal/test/issues/issue-illegal_enum_names/issue.gen.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/getkin/kin-openapi/openapi3" @@ -317,9 +318,9 @@ var swaggerSpec = []string{ "Jkt64H8BAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -334,9 +335,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go index 44651d421d..474f94ae9b 100644 --- a/internal/test/parameters/parameters.gen.go +++ b/internal/test/parameters/parameters.gen.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "github.com/deepmap/oapi-codegen/pkg/runtime" @@ -3134,9 +3135,9 @@ var swaggerSpec = []string{ "//8HeZ4w2yAAAA==", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -3151,9 +3152,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go index be7cb2cef9..b3400caea1 100644 --- a/internal/test/schemas/schemas.gen.go +++ b/internal/test/schemas/schemas.gen.go @@ -15,6 +15,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "gopkg.in/yaml.v2" @@ -1214,9 +1215,9 @@ var swaggerSpec = []string{ "3soZIRwum9e3wZ9YLw3EyhXN9rjMsmY7C/veVCLaktspV6Eg/wkAAP//VxOrc3sNAAA=", } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -1231,9 +1232,57 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index 9b9b70014d..5474eeece5 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -183,7 +183,7 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri var inlinedSpec string if opts.EmbedSpec { - inlinedSpec, err = GenerateInlinedSpec(t, swagger) + inlinedSpec, err = GenerateInlinedSpec(t, importMapping, swagger) if err != nil { return "", errors.Wrap(err, "error generating Go handlers for Paths") } diff --git a/pkg/codegen/inline.go b/pkg/codegen/inline.go index 76251c9a94..6a5f5dd0ce 100644 --- a/pkg/codegen/inline.go +++ b/pkg/codegen/inline.go @@ -26,7 +26,7 @@ import ( // This generates a gzipped, base64 encoded JSON representation of the // swagger definition, which we embed inside the generated code. -func GenerateInlinedSpec(t *template.Template, swagger *openapi3.Swagger) (string, error) { +func GenerateInlinedSpec(t *template.Template, importMapping importMap, swagger *openapi3.Swagger) (string, error) { // Marshal to json encoded, err := swagger.MarshalJSON() if err != nil { @@ -65,7 +65,10 @@ func GenerateInlinedSpec(t *template.Template, swagger *openapi3.Swagger) (strin // Generate inline code. buf.Reset() w := bufio.NewWriter(&buf) - err = t.ExecuteTemplate(w, "inline.tmpl", parts) + err = t.ExecuteTemplate(w, "inline.tmpl", struct { + SpecParts []string + ImportMapping importMap + }{SpecParts: parts, ImportMapping: importMapping}) if err != nil { return "", fmt.Errorf("error generating inlined spec: %s", err) } diff --git a/pkg/codegen/templates/inline.tmpl b/pkg/codegen/templates/inline.tmpl index 0ab3116ea4..26de489b2d 100644 --- a/pkg/codegen/templates/inline.tmpl +++ b/pkg/codegen/templates/inline.tmpl @@ -1,12 +1,12 @@ // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ -{{range .}} +{{range .SpecParts}} "{{.}}",{{end}} } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -21,9 +21,67 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + {{ if .ImportMapping }} + pathPrefix := path.Dir(pathToFile) + {{ end }} + {{ range $key, $value := .ImportMapping }} + for rawPath, rawFunc := range {{ $value.Name }}.PathToRawSpec(path.Join(pathPrefix, "{{ $key }}")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + {{- end }} + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadSwaggerFromData(specData) if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + return } diff --git a/pkg/codegen/templates/templates.gen.go b/pkg/codegen/templates/templates.gen.go index a8996d5421..6593050de7 100644 --- a/pkg/codegen/templates/templates.gen.go +++ b/pkg/codegen/templates/templates.gen.go @@ -751,13 +751,13 @@ import ( `, "inline.tmpl": `// Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ -{{range .}} +{{range .SpecParts}} "{{.}}",{{end}} } -// GetSwagger returns the Swagger specification corresponding to the generated code -// in this file. -func GetSwagger() (*openapi3.Swagger, error) { +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { return nil, fmt.Errorf("error base64 decoding spec: %s", err) @@ -772,11 +772,69 @@ func GetSwagger() (*openapi3.Swagger, error) { return nil, fmt.Errorf("error decompressing spec: %s", err) } - swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes()) + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + {{ if .ImportMapping }} + pathPrefix := path.Dir(pathToFile) + {{ end }} + {{ range $key, $value := .ImportMapping }} + for rawPath, rawFunc := range {{ $value.Name }}.PathToRawSpec(path.Join(pathPrefix, "{{ $key }}")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + {{- end }} + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.Swagger, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.SwaggerLoader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() if err != nil { - return nil, fmt.Errorf("error loading Swagger: %s", err) + return } - return swagger, nil + swagger, err = loader.LoadSwaggerFromData(specData) + if err != nil { + return + } + return } `, "param-types.tmpl": `{{range .}}{{$opid := .OperationId}}