Skip to content
Closed
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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,47 @@ This tells us that in order to resolve references generated from `some_spec.yaml
need to import `github.com/deepmap/some-package`. You may specify multiple mappings
by comma separating them in the form `key1:value1,key2:value2`.

### Replacing primitive types

Sometimes you would want to use a different type for the primitives (int, floats etc).
This can be done with the `primitive-mapping` option in the YAML configuration file.
A list of supported primitives is shown below.

If used inside a query parameter, the given replacement should implement `Binder`:
```go
type Binder interface {
Bind(string) error
}
```

| Primitive | Default type used |
| -- | -- |
| `int64` | `int64` |
| `int32` | `int32` |
| `int16` | `int16` |
| `int` | `int` |
| `uint64` | `uint64` |
| `uint32` | `uint32` |
| `uint16` | `uint16` |
| `uint` | `uint` |
| `double` | `float64` |
| `float` | `float32` |
| `bool` | `bool` |
| `byte` | `[]byte` |
| `email` | `openapi_types.Email` |
| `date` | `openapi_types.Date` |
| `date-time` | `time.Time` |
| `json` | `json.RawMessage` |

An example configuration:

```yaml
primitive-mapping:
int32: null.Int32
int64: null.Int64
int: float32
```

## What's missing or incomplete

This code is still young, and not complete, since we're filling it in as we
Expand Down
22 changes: 12 additions & 10 deletions cmd/oapi-codegen/oapi-codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,19 @@ var (
flagExcludeSchemas string
flagConfigFile string
flagAliasTypes bool
flagPrintVersion bool
flagPrintVersion bool
)

type configuration struct {
PackageName string `yaml:"package"`
GenerateTargets []string `yaml:"generate"`
OutputFile string `yaml:"output"`
IncludeTags []string `yaml:"include-tags"`
ExcludeTags []string `yaml:"exclude-tags"`
TemplatesDir string `yaml:"templates"`
ImportMapping map[string]string `yaml:"import-mapping"`
ExcludeSchemas []string `yaml:"exclude-schemas"`
PackageName string `yaml:"package"`
GenerateTargets []string `yaml:"generate"`
OutputFile string `yaml:"output"`
IncludeTags []string `yaml:"include-tags"`
ExcludeTags []string `yaml:"exclude-tags"`
TemplatesDir string `yaml:"templates"`
ImportMapping map[string]string `yaml:"import-mapping"`
PrimitiveMapping map[string]string `yaml:"primitive-mapping"`
ExcludeSchemas []string `yaml:"exclude-schemas"`
}

func main() {
Expand Down Expand Up @@ -104,7 +105,8 @@ func main() {
}

opts := codegen.Options{
AliasTypes: flagAliasTypes,
AliasTypes: flagAliasTypes,
PrimitiveMapping: cfg.PrimitiveMapping,
}
for _, g := range cfg.GenerateTargets {
switch g {
Expand Down
13 changes: 5 additions & 8 deletions internal/test/parameters/parameters.gen.go

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

37 changes: 20 additions & 17 deletions pkg/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ type Options struct {
SkipFmt bool // Whether to skip go imports on the generated code
SkipPrune bool // Whether to skip pruning unused components on the generated code
AliasTypes bool // Whether to alias types if possible
PrimitiveAliases bool // Use the primitive aliases declared in primitives.tmpl
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
PrimitiveMapping map[string]string // Override the primitive type with the given type.
ExcludeSchemas []string // Exclude from generation schemas with given names. Ignored when empty.
}

Expand Down Expand Up @@ -112,8 +114,9 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
}

// This creates the golang templates text package
TemplateFunctions["opts"] = func() Options { return opts }
t := template.New("oapi-codegen").Funcs(TemplateFunctions)
tmplFns := TemplateFunctions(&opts)
tmplFns["opts"] = func() Options { return opts }
t := template.New("oapi-codegen").Funcs(tmplFns)
// This parses all of our own template files into the template object
// above
t, err := templates.Parse(t)
Expand All @@ -131,14 +134,14 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
}
}

ops, err := OperationDefinitions(swagger)
ops, err := OperationDefinitions(swagger, &opts)
if err != nil {
return "", errors.Wrap(err, "error creating operation definitions")
}

var typeDefinitions, constantDefinitions string
if opts.GenerateTypes {
typeDefinitions, err = GenerateTypeDefinitions(t, swagger, ops, opts.ExcludeSchemas)
typeDefinitions, err = GenerateTypeDefinitions(t, swagger, ops, opts.ExcludeSchemas, &opts)
if err != nil {
return "", errors.Wrap(err, "error generating type definitions")
}
Expand Down Expand Up @@ -269,25 +272,25 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
return string(outBytes), nil
}

func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []OperationDefinition, excludeSchemas []string) (string, error) {
schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas)
func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []OperationDefinition, excludeSchemas []string, opts *Options) (string, error) {
schemaTypes, err := GenerateTypesForSchemas(t, swagger.Components.Schemas, excludeSchemas, opts)
if err != nil {
return "", errors.Wrap(err, "error generating Go types for component schemas")
}

paramTypes, err := GenerateTypesForParameters(t, swagger.Components.Parameters)
paramTypes, err := GenerateTypesForParameters(t, swagger.Components.Parameters, opts)
if err != nil {
return "", errors.Wrap(err, "error generating Go types for component parameters")
}
allTypes := append(schemaTypes, paramTypes...)

responseTypes, err := GenerateTypesForResponses(t, swagger.Components.Responses)
responseTypes, err := GenerateTypesForResponses(t, swagger.Components.Responses, opts)
if err != nil {
return "", errors.Wrap(err, "error generating Go types for component responses")
}
allTypes = append(allTypes, responseTypes...)

bodyTypes, err := GenerateTypesForRequestBodies(t, swagger.Components.RequestBodies)
bodyTypes, err := GenerateTypesForRequestBodies(t, swagger.Components.RequestBodies, opts)
if err != nil {
return "", errors.Wrap(err, "error generating Go types for component request bodies")
}
Expand Down Expand Up @@ -359,7 +362,7 @@ func GenerateConstants(t *template.Template, ops []OperationDefinition) (string,

// Generates type definitions for any custom types defined in the
// components/schemas section of the Swagger spec.
func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) ([]TypeDefinition, error) {
func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string, opts *Options) ([]TypeDefinition, error) {
var excludeSchemasMap = make(map[string]bool)
for _, schema := range excludeSchemas {
excludeSchemasMap[schema] = true
Expand All @@ -372,7 +375,7 @@ func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.
}
schemaRef := schemas[schemaName]

goSchema, err := GenerateGoSchema(schemaRef, []string{schemaName})
goSchema, err := GenerateGoSchema(schemaRef, []string{schemaName}, opts)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error converting Schema %s to Go type", schemaName))
}
Expand All @@ -390,12 +393,12 @@ func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.

// Generates type definitions for any custom types defined in the
// components/parameters section of the Swagger spec.
func GenerateTypesForParameters(t *template.Template, params map[string]*openapi3.ParameterRef) ([]TypeDefinition, error) {
func GenerateTypesForParameters(t *template.Template, params map[string]*openapi3.ParameterRef, opts *Options) ([]TypeDefinition, error) {
var types []TypeDefinition
for _, paramName := range SortedParameterKeys(params) {
paramOrRef := params[paramName]

goType, err := paramToGoType(paramOrRef.Value, nil)
goType, err := paramToGoType(paramOrRef.Value, nil, opts)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in parameter %s", paramName))
}
Expand All @@ -422,7 +425,7 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi

// Generates type definitions for any custom types defined in the
// components/responses section of the Swagger spec.
func GenerateTypesForResponses(t *template.Template, responses openapi3.Responses) ([]TypeDefinition, error) {
func GenerateTypesForResponses(t *template.Template, responses openapi3.Responses, opts *Options) ([]TypeDefinition, error) {
var types []TypeDefinition

for _, responseName := range SortedResponsesKeys(responses) {
Expand All @@ -434,7 +437,7 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response
response := responseOrRef.Value
jsonResponse, found := response.Content["application/json"]
if found {
goType, err := GenerateGoSchema(jsonResponse.Schema, []string{responseName})
goType, err := GenerateGoSchema(jsonResponse.Schema, []string{responseName}, opts)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in response %s", responseName))
}
Expand All @@ -461,7 +464,7 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response

// Generates type definitions for any custom types defined in the
// components/requestBodies section of the Swagger spec.
func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*openapi3.RequestBodyRef) ([]TypeDefinition, error) {
func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*openapi3.RequestBodyRef, opts *Options) ([]TypeDefinition, error) {
var types []TypeDefinition

for _, bodyName := range SortedRequestBodyKeys(bodies) {
Expand All @@ -472,7 +475,7 @@ func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*open
response := bodyOrRef.Value
jsonBody, found := response.Content["application/json"]
if found {
goType, err := GenerateGoSchema(jsonBody.Schema, []string{bodyName})
goType, err := GenerateGoSchema(jsonBody.Schema, []string{bodyName}, opts)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error generating Go type for schema in body %s", bodyName))
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/codegen/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ func (p ParameterDefinitions) FindByName(name string) *ParameterDefinition {
// This function walks the given parameters dictionary, and generates the above
// descriptors into a flat list. This makes it a lot easier to traverse the
// data in the template engine.
func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterDefinition, error) {
func DescribeParameters(params openapi3.Parameters, path []string, opts *Options) ([]ParameterDefinition, error) {
outParams := make([]ParameterDefinition, 0)
for _, paramOrRef := range params {
param := paramOrRef.Value

goType, err := paramToGoType(param, append(path, param.Name))
goType, err := paramToGoType(param, append(path, param.Name), opts)
if err != nil {
return nil, fmt.Errorf("error generating type for param (%s): %s",
param.Name, err)
Expand Down Expand Up @@ -259,7 +259,7 @@ func (o *OperationDefinition) SummaryAsComment() string {
// types which we know how to parse. These will be turned into fields on a
// response object for automatic deserialization of responses in the generated
// Client code. See "client-with-responses.tmpl".
func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefinition, error) {
func (o *OperationDefinition) GetResponseTypeDefinitions(opts *Options) ([]ResponseTypeDefinition, error) {
var tds []ResponseTypeDefinition

responses := o.Spec.Responses
Expand All @@ -274,7 +274,7 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini
contentType := responseRef.Value.Content[contentTypeName]
// We can only generate a type if we have a schema:
if contentType.Schema != nil {
responseSchema, err := GenerateGoSchema(contentType.Schema, []string{responseName})
responseSchema, err := GenerateGoSchema(contentType.Schema, []string{responseName}, opts)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("Unable to determine Go type for %s.%s", o.OperationId, contentTypeName))
}
Expand Down Expand Up @@ -375,14 +375,14 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []
}

// OperationDefinitions returns all operations for a swagger definition.
func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
func OperationDefinitions(swagger *openapi3.T, opts *Options) ([]OperationDefinition, error) {
var operations []OperationDefinition

for _, requestPath := range SortedPathsKeys(swagger.Paths) {
pathItem := swagger.Paths[requestPath]
// These are parameters defined for all methods on a given path. They
// are shared by all methods.
globalParams, err := DescribeParameters(pathItem.Parameters, nil)
globalParams, err := DescribeParameters(pathItem.Parameters, nil, opts)
if err != nil {
return nil, fmt.Errorf("error describing global parameters for %s: %s",
requestPath, err)
Expand All @@ -409,7 +409,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {

// These are parameters defined for the specific path method that
// we're iterating over.
localParams, err := DescribeParameters(op.Parameters, []string{op.OperationID + "Params"})
localParams, err := DescribeParameters(op.Parameters, []string{op.OperationID + "Params"}, opts)
if err != nil {
return nil, fmt.Errorf("error describing global parameters for %s/%s: %s",
opName, requestPath, err)
Expand All @@ -427,7 +427,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
return nil, err
}

bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(op.OperationID, op.RequestBody)
bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(op.OperationID, op.RequestBody, opts)
if err != nil {
return nil, errors.Wrap(err, "error generating body definitions")
}
Expand Down Expand Up @@ -496,7 +496,7 @@ func generateDefaultOperationID(opName string, requestPath string) (string, erro

// This function turns the Swagger body definitions into a list of our body
// definitions which will be used for code generation.
func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBodyRef) ([]RequestBodyDefinition, []TypeDefinition, error) {
func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBodyRef, opts *Options) ([]RequestBodyDefinition, []TypeDefinition, error) {
if bodyOrRef == nil {
return nil, nil, nil
}
Expand All @@ -518,7 +518,7 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody
}

bodyTypeName := operationID + tag + "Body"
bodySchema, err := GenerateGoSchema(content.Schema, []string{bodyTypeName})
bodySchema, err := GenerateGoSchema(content.Schema, []string{bodyTypeName}, opts)
if err != nil {
return nil, nil, errors.Wrap(err, "error generating request body definition")
}
Expand Down
Loading