Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions cmd/oapi-codegen/oapi-codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
Expand All @@ -33,12 +34,13 @@ func errExit(format string, args ...interface{}) {

func main() {
var (
packageName string
generate string
outputFile string
includeTags string
excludeTags string
templatesDir string
packageName string
generate string
outputFile string
includeTags string
excludeTags string
templatesDir string
importMapping string
)
flag.StringVar(&packageName, "package", "", "The package name for generated code")
flag.StringVar(&generate, "generate", "types,client,server,spec",
Expand All @@ -47,6 +49,7 @@ func main() {
flag.StringVar(&includeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
flag.StringVar(&excludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
flag.StringVar(&templatesDir, "templates", "", "Path to directory containing user templates")
flag.StringVar(&importMapping, "import-mapping", "", "A dict from the external reference to golang package path")
flag.Parse()

if flag.NArg() < 1 {
Expand Down Expand Up @@ -106,6 +109,13 @@ func main() {
}
opts.UserTemplates = templates

opts.ImportMapping = map[string]string{}
if len(importMapping) > 0 {
if err = json.Unmarshal([]byte(importMapping), &opts.ImportMapping); err != nil {
errExit("error parsing import-mapping: %s\n", err)
}
}

code, err := codegen.Generate(swagger, packageName, opts)
if err != nil {
errExit("error generating code: %s\n", err)
Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/getkin/kin-openapi v0.3.0 h1:xsQ4mA20YJDMgIHdHqMKZ66QUe6/hi+x6yLsTTz8xyQ=
github.com/getkin/kin-openapi v0.3.0/go.mod h1:W8dhxZgpE84ciM+VIItFqkmZ4eHtuomrdIHtASQIqi0=
github.com/getkin/kin-openapi v0.13.0 h1:03fqBEEgivp4MVK2ElB140B56hjO9ZFvFTHBsvFsSro=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
Expand Down Expand Up @@ -43,8 +41,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
Expand Down Expand Up @@ -89,7 +85,5 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
3 changes: 3 additions & 0 deletions internal/test/externalref/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package externalref

//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=externalref -o externalref.gen.go --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"} spec.yaml
15 changes: 15 additions & 0 deletions internal/test/externalref/externalref.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions internal/test/externalref/imports_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package externalref

import (
"testing"

"github.com/deepmap/oapi-codegen/internal/test/externalref/packageA"
"github.com/deepmap/oapi-codegen/internal/test/externalref/packageB"
)

func TestParameters(t *testing.T) {
b := &packageB.ObjectB{}
_ = Container{
ObjectA: &packageA.ObjectA{ObjectB: b},
ObjectB: b,
}
}
3 changes: 3 additions & 0 deletions internal/test/externalref/packageA/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +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
14 changes: 14 additions & 0 deletions internal/test/externalref/packageA/externalref.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions internal/test/externalref/packageA/spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
components:
schemas:
ObjectA:
properties:
name:
type: string
object_b:
$ref: ../packageB/spec.yaml#/components/schemas/ObjectB
3 changes: 3 additions & 0 deletions internal/test/externalref/packageB/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +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
9 changes: 9 additions & 0 deletions internal/test/externalref/packageB/externalref.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions internal/test/externalref/packageB/spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
components:
schemas:
ObjectB:
properties:
name:
type: string
11 changes: 11 additions & 0 deletions internal/test/externalref/spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
openapi: "3.0.0"
info: {}
paths: {}
components:
schemas:
Container:
properties:
object_a:
$ref: ./packageA/spec.yaml#/components/schemas/ObjectA
object_b:
$ref: ./packageB/spec.yaml#/components/schemas/ObjectB
33 changes: 33 additions & 0 deletions pkg/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Options struct {
IncludeTags []string // Only include operations that have one of these tags. Ignored when empty.
ExcludeTags []string // Exclude operations that have one of these tags. Ignored when empty.
UserTemplates map[string]string // Override built-in templates from user-provided files
ImportMapping map[string]string // ImportMapping specifies the golang package path for each external reference
}

type goImport struct {
Expand Down Expand Up @@ -84,12 +85,41 @@ var (
{lookFor: "xml\\.", packageName: "encoding/xml"},
{lookFor: "yaml\\.", packageName: "gopkg.in/yaml.v2"},
}

importMapping = map[string]goImport{}
)

func constructImportMapping(input map[string]string) map[string]goImport {
var (
nameToAlias = map[string]string{}
result = map[string]goImport{}
)

{
var packagePaths []string
for _, packageName := range input {
packagePaths = append(packagePaths, packageName)
}
sort.Strings(packagePaths)

for _, packageName := range packagePaths {
if _, ok := nameToAlias[packageName]; !ok {
nameToAlias[packageName] = fmt.Sprintf("externalRef%d", len(nameToAlias))
}
}
}
for urlOrPath, packageName := range input {
result[urlOrPath] = goImport{alias: nameToAlias[packageName], packageName: packageName}
}
return result
}

// Uses the Go templating engine to generate all of our server wrappers from
// the descriptions we've built up above from the schema objects.
// opts defines
func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (string, error) {
importMapping = constructImportMapping(opts.ImportMapping)

filterOperationsByTag(swagger, opts)
if !opts.SkipPrune {
pruneUnusedComponents(swagger)
Expand Down Expand Up @@ -169,6 +199,9 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri

// Imports needed for the generated code to compile
var imports []string
for _, importGo := range importMapping {
imports = append(imports, importGo.String())
}

var buf bytes.Buffer
w := bufio.NewWriter(&buf)
Expand Down
37 changes: 24 additions & 13 deletions pkg/codegen/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func ToCamelCase(str string) string {
if unicode.IsUpper(v) {
n += string(v)
}
if unicode.IsDigit(v) {
if unicode.IsDigit(v) {
n += string(v)
}
if unicode.IsLower(v) {
Expand All @@ -76,7 +76,7 @@ func ToCamelCase(str string) string {
}
}

if strings.ContainsRune(separators, v) {
if strings.ContainsRune(separators, v) {
capNext = true
} else {
capNext = false
Expand Down Expand Up @@ -197,20 +197,31 @@ func StringInArray(str string, array []string) bool {
// #/components/schemas/Foo -> Foo
// #/components/parameters/Bar -> Bar
// #/components/responses/Baz -> Baz
// Remote components (document.json#/Foo) are not yet supported
// URL components (http://deepmap.com/schemas/document.json#Foo) are not yet
// supported
// We only support flat components for now, so no components in a schema under
// components.
// Remote components (document.json#/Foo) are supported if they present in --import-mapping
// URL components (http://deepmap.com/schemas/document.json#Foo) are supported if they present in --import-mapping
//
func RefPathToGoType(refPath string) (string, error) {
pathParts := strings.Split(refPath, "/")
if pathParts[0] != "#" {
return "", errors.New("Only local document components are supported")
if refPath[0] == '#' {
pathParts := strings.Split(refPath, "/")
if len(pathParts) != 4 {
return "", errors.New("Parameter nesting is deeper than supported")
}
return SchemaNameToTypeName(pathParts[3]), nil
}
if len(pathParts) != 4 {
return "", errors.New("Parameter nesting is deeper than supported")
pathParts := strings.Split(refPath, "#")
if len(pathParts) != 2 {
return "", fmt.Errorf("unsupported reference: %s", refPath)
}
remoteComponent, flatComponent := pathParts[0], pathParts[1]
if goImport, ok := importMapping[remoteComponent]; !ok {
return "", fmt.Errorf("unrecognized external reference '%s'; please provide the known import for this reference using option --import-mapping", remoteComponent)
} else {
goType, err := RefPathToGoType("#" + flatComponent)
if err != nil {
return "", err
}
return fmt.Sprintf("%s.%s", goImport.alias, goType), nil
}
return SchemaNameToTypeName(pathParts[3]), nil
}

// This function converts a swagger style path URI with parameters to a
Expand Down
30 changes: 5 additions & 25 deletions pkg/util/loader.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
package util

import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"

"github.com/getkin/kin-openapi/openapi3"
)

func LoadSwagger(filePath string) (*openapi3.Swagger, error) {
data, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}

var swagger *openapi3.Swagger
ext := filepath.Ext(filePath)
ext = strings.ToLower(ext)
switch ext {
// The YAML handler can parse both YAML and JSON
case ".yaml", ".yml", ".json":
swagger, err = openapi3.NewSwaggerLoader().LoadSwaggerFromData(data)
default:
return nil, fmt.Errorf("%s is not a supported extension, use .yaml, .yml or .json", ext)
}
if err != nil {
return nil, err
}
return swagger, nil
func LoadSwagger(filePath string) (swagger *openapi3.Swagger, err error) {
loader := openapi3.NewSwaggerLoader()
loader.IsExternalRefsAllowed = true
swagger, err = loader.LoadSwaggerFromFile(filePath)
return
}