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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,11 @@ When you've got a large OpenAPI specification, you may find it useful to split t

This is supported by `oapi-codegen`, through the ability to perform "Import Mapping".

> [!NOTE]
> The keys of `import-mapping` are the paths of the `$ref`'d documents — a relative file path or a URL, exactly as it is written in the `$ref` — and the values are the Go packages their types are generated into.
>
> A key cannot be a JSON pointer such as `#/components/schemas`. References within a single document always resolve to the package being generated, so there's nothing to map — if you want your models in a different Go package to your server, split them into their own spec file and map that file, as shown below.

For instance, let's say that we have a large API, which has a user-facing API and an admin API, both of which use a common set of API models.

In this case, we may have an Admin API that looks like:
Expand Down
11 changes: 11 additions & 0 deletions pkg/codegen/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"regexp"
"strings"
)

// defaultStreamingContentTypes are the regex patterns matched against
Expand Down Expand Up @@ -98,6 +99,16 @@ func (o Configuration) Validate() error {
}
}

// import-mapping keys are the paths of $ref'd documents (a relative
// file path or URL). A JSON pointer key can never match anything —
// references within the same document always resolve to the package
// being generated — so it is a configuration mistake, not a mapping.
for _, specPath := range SortedMapKeys(o.ImportMapping) {
if strings.HasPrefix(specPath, "#") {
errs = append(errs, fmt.Errorf("`import-mapping` key %q is a JSON pointer, but keys must be the path or URL of a $ref'd document; references within the same document cannot be remapped to another package — see https://github.com/oapi-codegen/oapi-codegen/tree/main/examples/import-mapping", specPath))
}
}

err := errors.Join(errs...)
if err != nil {
return fmt.Errorf("failed to validate configuration: %w", err)
Expand Down
34 changes: 34 additions & 0 deletions pkg/codegen/configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package codegen

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestConfigurationValidateImportMappingKeys verifies that import-mapping
// keys which are JSON pointers are rejected: keys must be the path or URL of
// a $ref'd document, and same-document references can never be remapped.
// Please see https://github.com/oapi-codegen/oapi-codegen/issues/2459
func TestConfigurationValidateImportMappingKeys(t *testing.T) {
cfg := Configuration{
PackageName: "api",
Generate: GenerateOptions{Models: true},
ImportMapping: map[string]string{
"#/components/schemas": "example.com/mymodule/dto",
},
}
err := cfg.Validate()
require.Error(t, err)
assert.Contains(t, err.Error(), "JSON pointer")
assert.Contains(t, err.Error(), `"#/components/schemas"`)

// Document paths and URLs are fine, as is the current-package mapping.
cfg.ImportMapping = map[string]string{
"../common/api.yaml": "example.com/mymodule/common",
"https://example.com/api.yaml": "example.com/mymodule/remote",
"./sibling.yaml": "-",
}
require.NoError(t, cfg.Validate())
}