Skip to content

Commit

Permalink
feat: allow import-mapping to the current package (oapi-codegen#1774)
Browse files Browse the repository at this point in the history
To simplify working with large OpenAPI specs, a good option is to split
this into multiple files.

However, a long-standing issue with `oapi-codegen`, as noted in oapi-codegen#400, is
that when using multiple files for your OpenAPI specifications, the
Import Mapping functionality requires splitting each file into a
separate package.

To improve this experience, we can allow using a new "magic" package
name, `-`, and omit the need to perform package imports.

This also updates our existing `import-mapping` example, and adds a new
example for this functionality, as well as clarifying this in the docs.

We simplify the existing `import-mapping` example, as the duplicated
`User` type causes compilation issues.

To actually implement this, we need to tweak our `refPathToGoType`
logic.

Closes oapi-codegen#400.

Co-authored-by: Dj Gilcrease <[email protected]>
  • Loading branch information
jamietanna and djgilcrease authored Sep 20, 2024
1 parent 891a067 commit 3aa1efb
Show file tree
Hide file tree
Showing 18 changed files with 452 additions and 62 deletions.
93 changes: 80 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1824,10 +1824,13 @@ For a complete example see [`examples/only-models`](examples/only-models).
When you've got a large OpenAPI specification, you may find it useful to split the contents of the spec across multiple files, using external references, such as:

```yaml
components:
schemas:
User:
$ref: '../common/api.yaml#/components/schemas/User'
responses:
200:
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
```

This is supported by `oapi-codegen`, through the ability to perform "Import Mapping".
Expand Down Expand Up @@ -1869,11 +1872,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
$ref: '../common/api.yaml#/components/schemas/User'
$ref: '../common/api.yaml#/components/schemas/User'
```

This references the common spec:
Expand All @@ -1892,7 +1891,75 @@ components:
- name
```

And finally we have our configuration file:
So how do we get `oapi-codegen` to generate our code?

### Using a single package, with multiple OpenAPI spec for a given package

<a name=import-mapping-self></a>

> [!TIP]
> Since `oapi-codegen` v2.4.0, it is now possible to split large OpenAPI specifications into the same Go package, using the "self" mapping (denoted by a `-`) when using Import Mapping.
>
> This is an improvement on the previous model, which would require splitting files across multiple packages.

> [!NOTE]
> You still need to have multiple `go generate`s, and any other configuration files.

To get `oapi-codegen`'s single-package support working, we need multiple calls to `oapi-codegen`, one call per OpenAPI spec file:

```sh
$ go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg-api.yaml ../admin/api.yaml
$ go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg-user.yaml ../common/api.yaml
```

This therefore means that we need multiple configuration files, such as `cfg-api.yaml`:

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: samepackage
output: server.gen.go
generate:
models: true
chi-server: true
strict-server: true
output-options:
# to make sure that all types are generated
skip-prune: true
import-mapping:
user.yaml: "-"
```

And then our `cfg-user.yaml`:

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
package: samepackage
output: user.gen.go
generate:
models: true
output-options:
# to make sure that all types are generated
skip-prune: true
```

From here, `oapi-codegen` will generate multiple Go files, all within the same package, which can be used to break down your large OpenAPI specifications, and generate only the subsets of code needed for each part of the spec.

Check out [the import-mapping/samepackage example](examples/import-mapping/samepackage) for the full code.

### Using multiple packages, with one OpenAPI spec per package

To get `oapi-codegen`'s multi-package support working, we need to set up our directory structure:

```
├── admin
│   ├── cfg.yaml
│   └── generate.go
└── common
├── cfg.yaml
   └── generate.go
```

We could start with our configuration file for our admin API spec:

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
Expand All @@ -1914,7 +1981,7 @@ If we were to run `oapi-codegen`, this will fail with the following error
error generating code: error creating operation definitions: error generating response definitions: error generating request body definition: error turning reference (../common/api.yaml#/components/schemas/User) into a Go type: unrecognized external reference '../common/api.yaml'; please provide the known import for this reference using option --import-mapping
```

This is because `oapi-codegen` requires:
This is because `oapi-codegen` requires the `import-mapping`:

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/HEAD/configuration-schema.json
Expand Down Expand Up @@ -1945,9 +2012,9 @@ import (
type User = externalRef0.User
```

If you don't want to do this, an alternate option is to [bundle your multiple OpenAPI files](https://www.jvt.me/posts/2022/02/10/bundle-openapi/) into a single spec.
If you don't want to do this, an alternate option is to [use a single package, with multiple OpenAPI spec files for that given package](#import-mapping-self) or to [bundle your multiple OpenAPI files](https://www.jvt.me/posts/2022/02/10/bundle-openapi/) into a single spec.

Check out [the import-mapping example](examples/import-mapping/) for the full code.
Check out [the import-mapping/multiplepackages example](examples/import-mapping/multiplepackages/) for the full code.

## Modifying the input OpenAPI Specification

Expand Down
2 changes: 1 addition & 1 deletion configuration-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@
"type": "object",
"additionalProperties": {
"type": "string",
"description": "ImportMapping specifies the golang package path for each external reference"
"description": "ImportMapping specifies the golang package path for each external reference. A value of `-` will indicate that the current package will be used"
}
},
"additional-imports": {
Expand Down
6 changes: 1 addition & 5 deletions examples/import-mapping/admin/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,4 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
$ref: '../common/api.yaml#/components/schemas/User'
$ref: '../common/api.yaml#/components/schemas/User'
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ output-options:
# to make sure that all types are generated
skip-prune: true
import-mapping:
../common/api.yaml: github.com/oapi-codegen/oapi-codegen/v2/examples/import-mapping/common
../common/api.yaml: github.com/oapi-codegen/oapi-codegen/v2/examples/import-mapping/multiplepackages/common
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package admin

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml ../../admin/api.yaml

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

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package common

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml ../../common/api.yaml
File renamed without changes.
12 changes: 12 additions & 0 deletions examples/import-mapping/samepackage/cfg-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# yaml-language-server: $schema=../../../configuration-schema.json
package: samepackage
output: server.gen.go
generate:
models: true
chi-server: true
strict-server: true
output-options:
# to make sure that all types are generated
skip-prune: true
import-mapping:
../common/api.yaml: "-"
8 changes: 8 additions & 0 deletions examples/import-mapping/samepackage/cfg-user.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# yaml-language-server: $schema=../../../configuration-schema.json
package: samepackage
output: user.gen.go
generate:
models: true
output-options:
# to make sure that all types are generated
skip-prune: true
4 changes: 4 additions & 0 deletions examples/import-mapping/samepackage/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package samepackage

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg-api.yaml ../admin/api.yaml
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg-user.yaml ../common/api.yaml
Loading

0 comments on commit 3aa1efb

Please sign in to comment.