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
8 changes: 8 additions & 0 deletions internal/test/issues/issue-2430/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# yaml-language-server: $schema=../../../../configuration-schema.json
package: issue2430
output: issue2430.gen.go
generate:
models: true
output-options:
skip-prune: true
3 changes: 3 additions & 0 deletions internal/test/issues/issue-2430/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package issue2430

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml
19 changes: 19 additions & 0 deletions internal/test/issues/issue-2430/issue2430.gen.go

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

37 changes: 37 additions & 0 deletions internal/test/issues/issue-2430/issue2430_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package issue2430

import (
"encoding/json"
"testing"

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

// Regression test for https://github.com/oapi-codegen/oapi-codegen/issues/2430.
//
// OpenAPI 3.1 allows a schema whose only type is "null" (as opposed to a
// type array that merely includes "null"). Such a schema validates exactly
// the JSON value null. The generator used to fail with "unhandled Schema
// type: &[null]"; it now maps the schema to `any`, with no optional
// pointer, since nil is already `any`'s zero value.

func TestNullTypePropertyRoundTrip(t *testing.T) {
var v ChallengeOpenJson
require.NoError(t, json.Unmarshal([]byte(`{"id":"1","challenger":null}`), &v))
assert.Equal(t, "1", v.Id)
assert.Nil(t, v.Challenger)

out, err := json.Marshal(RequiredNull{})
require.NoError(t, err)
// The required null-typed field must serialize as an explicit null.
assert.JSONEq(t, `{"value":null}`, string(out))
}

func TestNullTypeComponentIsAny(t *testing.T) {
// NullOnly is an alias for `any`; assigning an arbitrary value must
// compile, and nil round-trips through JSON as null.
var n NullOnly
require.NoError(t, json.Unmarshal([]byte(`null`), &n))
assert.Nil(t, n)
}
33 changes: 33 additions & 0 deletions internal/test/issues/issue-2430/spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
openapi: 3.1.0
info:
title: issue-2430
version: 1.0.0
paths: {}
components:
schemas:
# A property whose schema is the bare OpenAPI 3.1 null type: the only
# valid JSON value for `challenger` is null. Maps to `any` in Go.
ChallengeOpenJson:
type: object
required:
- id
properties:
id:
type: string
url:
type: string
format: uri
challenger:
type: "null"
# A required null-typed property: must not be wrapped in a pointer,
# since `any` already has nil as its zero value.
RequiredNull:
type: object
required:
- value
properties:
value:
type: "null"
# A named component that is itself the bare null type.
NullOnly:
type: "null"
12 changes: 12 additions & 0 deletions pkg/codegen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,18 @@ func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schem
outSchema.SkipOptionalPointer = true
}
outSchema.DefineViaAlias = true
} else if t.Is("null") {
// OpenAPI 3.1 allows a schema whose only type is "null", which
// validates exactly the JSON value null. Go has no type whose only
// value is null, so map it to `any` — the same permissive mapping
// used for typeless schemas — rather than rejecting an otherwise
// valid spec. An optional pointer makes no sense for `any`, whose
// zero value is already nil, so skip it — mirroring the
// typeless-schema handling in GenerateGoSchema.
// See https://github.com/oapi-codegen/oapi-codegen/issues/2430
outSchema.GoType = "any"
outSchema.SkipOptionalPointer = true
outSchema.DefineViaAlias = true
} else {
return fmt.Errorf("unhandled Schema type: %v", t)
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/codegen/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,15 @@ func TestProperty_ZeroValueIsNil(t *testing.T) {
})
}
}

// A bare OpenAPI 3.1 `type: "null"` schema validates exactly the JSON
// value null. Go has no such type, so it maps to `any`, with the optional
// pointer skipped (nil is already `any`'s zero value). Issue #2430.
func TestOapiSchemaToGoType_NullType(t *testing.T) {
schema := &openapi3.Schema{Type: &openapi3.Types{"null"}}
var out Schema
require.NoError(t, oapiSchemaToGoType(schema, []string{"Challenger"}, &out))
assert.Equal(t, "any", out.GoType)
assert.True(t, out.SkipOptionalPointer)
assert.True(t, out.DefineViaAlias)
}