diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go index ad9f78c0df..0e9ea89261 100644 --- a/cmd/oapi-codegen/oapi-codegen.go +++ b/cmd/oapi-codegen/oapi-codegen.go @@ -258,7 +258,7 @@ func main() { return } - swagger, err := util.LoadSwagger(flag.Arg(0)) + swagger, err := util.LoadSwaggerWithCircularReferenceCount(flag.Arg(0), opts.Compatibility.CircularReferenceLimit) if err != nil { errExit("error loading swagger spec in %s\n: %s", flag.Arg(0), err) } diff --git a/internal/test/issues/issue-936/api.gen.go b/internal/test/issues/issue-936/api.gen.go new file mode 100644 index 0000000000..15af391c65 --- /dev/null +++ b/internal/test/issues/issue-936/api.gen.go @@ -0,0 +1,432 @@ +// Package issue936 provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT. +package issue936 + +import ( + "encoding/json" + + "github.com/deepmap/oapi-codegen/pkg/runtime" +) + +// FilterPredicate defines model for FilterPredicate. +type FilterPredicate struct { + union json.RawMessage +} + +// FilterPredicate1 defines model for . +type FilterPredicate1 = []FilterPredicate + +// FilterPredicateOp defines model for FilterPredicateOp. +type FilterPredicateOp struct { + Any *FilterPredicateOp_Any `json:"$any,omitempty"` + None *FilterPredicateOp_None `json:"$none,omitempty"` +} + +// FilterPredicateOpAny0 defines model for . +type FilterPredicateOpAny0 = []FilterPredicate + +// FilterPredicateOp_Any defines model for FilterPredicateOp.Any. +type FilterPredicateOp_Any struct { + union json.RawMessage +} + +// FilterPredicateOpNone1 defines model for . +type FilterPredicateOpNone1 = []FilterPredicate + +// FilterPredicateOp_None defines model for FilterPredicateOp.None. +type FilterPredicateOp_None struct { + union json.RawMessage +} + +// FilterPredicateRangeOp defines model for FilterPredicateRangeOp. +type FilterPredicateRangeOp struct { + Lt *FilterRangeValue `json:"$lt,omitempty"` +} + +// FilterRangeValue defines model for FilterRangeValue. +type FilterRangeValue struct { + union json.RawMessage +} + +// FilterRangeValue0 defines model for . +type FilterRangeValue0 = float32 + +// FilterRangeValue1 defines model for . +type FilterRangeValue1 = string + +// FilterValue defines model for FilterValue. +type FilterValue struct { + union json.RawMessage +} + +// FilterValue0 defines model for . +type FilterValue0 = float32 + +// FilterValue1 defines model for . +type FilterValue1 = string + +// FilterValue2 defines model for . +type FilterValue2 = bool + +// AsFilterValue returns the union data inside the FilterPredicate as a FilterValue +func (t FilterPredicate) AsFilterValue() (FilterValue, error) { + var body FilterValue + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterValue overwrites any union data inside the FilterPredicate as the provided FilterValue +func (t *FilterPredicate) FromFilterValue(v FilterValue) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterValue performs a merge with any union data inside the FilterPredicate, using the provided FilterValue +func (t *FilterPredicate) MergeFilterValue(v FilterValue) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterPredicate1 returns the union data inside the FilterPredicate as a FilterPredicate1 +func (t FilterPredicate) AsFilterPredicate1() (FilterPredicate1, error) { + var body FilterPredicate1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicate1 overwrites any union data inside the FilterPredicate as the provided FilterPredicate1 +func (t *FilterPredicate) FromFilterPredicate1(v FilterPredicate1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicate1 performs a merge with any union data inside the FilterPredicate, using the provided FilterPredicate1 +func (t *FilterPredicate) MergeFilterPredicate1(v FilterPredicate1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterPredicateOp returns the union data inside the FilterPredicate as a FilterPredicateOp +func (t FilterPredicate) AsFilterPredicateOp() (FilterPredicateOp, error) { + var body FilterPredicateOp + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicateOp overwrites any union data inside the FilterPredicate as the provided FilterPredicateOp +func (t *FilterPredicate) FromFilterPredicateOp(v FilterPredicateOp) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicateOp performs a merge with any union data inside the FilterPredicate, using the provided FilterPredicateOp +func (t *FilterPredicate) MergeFilterPredicateOp(v FilterPredicateOp) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterPredicateRangeOp returns the union data inside the FilterPredicate as a FilterPredicateRangeOp +func (t FilterPredicate) AsFilterPredicateRangeOp() (FilterPredicateRangeOp, error) { + var body FilterPredicateRangeOp + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicateRangeOp overwrites any union data inside the FilterPredicate as the provided FilterPredicateRangeOp +func (t *FilterPredicate) FromFilterPredicateRangeOp(v FilterPredicateRangeOp) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicateRangeOp performs a merge with any union data inside the FilterPredicate, using the provided FilterPredicateRangeOp +func (t *FilterPredicate) MergeFilterPredicateRangeOp(v FilterPredicateRangeOp) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t FilterPredicate) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FilterPredicate) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsFilterPredicateOpAny0 returns the union data inside the FilterPredicateOp_Any as a FilterPredicateOpAny0 +func (t FilterPredicateOp_Any) AsFilterPredicateOpAny0() (FilterPredicateOpAny0, error) { + var body FilterPredicateOpAny0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicateOpAny0 overwrites any union data inside the FilterPredicateOp_Any as the provided FilterPredicateOpAny0 +func (t *FilterPredicateOp_Any) FromFilterPredicateOpAny0(v FilterPredicateOpAny0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicateOpAny0 performs a merge with any union data inside the FilterPredicateOp_Any, using the provided FilterPredicateOpAny0 +func (t *FilterPredicateOp_Any) MergeFilterPredicateOpAny0(v FilterPredicateOpAny0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t FilterPredicateOp_Any) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FilterPredicateOp_Any) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsFilterPredicate returns the union data inside the FilterPredicateOp_None as a FilterPredicate +func (t FilterPredicateOp_None) AsFilterPredicate() (FilterPredicate, error) { + var body FilterPredicate + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicate overwrites any union data inside the FilterPredicateOp_None as the provided FilterPredicate +func (t *FilterPredicateOp_None) FromFilterPredicate(v FilterPredicate) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicate performs a merge with any union data inside the FilterPredicateOp_None, using the provided FilterPredicate +func (t *FilterPredicateOp_None) MergeFilterPredicate(v FilterPredicate) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterPredicateOpNone1 returns the union data inside the FilterPredicateOp_None as a FilterPredicateOpNone1 +func (t FilterPredicateOp_None) AsFilterPredicateOpNone1() (FilterPredicateOpNone1, error) { + var body FilterPredicateOpNone1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterPredicateOpNone1 overwrites any union data inside the FilterPredicateOp_None as the provided FilterPredicateOpNone1 +func (t *FilterPredicateOp_None) FromFilterPredicateOpNone1(v FilterPredicateOpNone1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterPredicateOpNone1 performs a merge with any union data inside the FilterPredicateOp_None, using the provided FilterPredicateOpNone1 +func (t *FilterPredicateOp_None) MergeFilterPredicateOpNone1(v FilterPredicateOpNone1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t FilterPredicateOp_None) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FilterPredicateOp_None) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsFilterRangeValue0 returns the union data inside the FilterRangeValue as a FilterRangeValue0 +func (t FilterRangeValue) AsFilterRangeValue0() (FilterRangeValue0, error) { + var body FilterRangeValue0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterRangeValue0 overwrites any union data inside the FilterRangeValue as the provided FilterRangeValue0 +func (t *FilterRangeValue) FromFilterRangeValue0(v FilterRangeValue0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterRangeValue0 performs a merge with any union data inside the FilterRangeValue, using the provided FilterRangeValue0 +func (t *FilterRangeValue) MergeFilterRangeValue0(v FilterRangeValue0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterRangeValue1 returns the union data inside the FilterRangeValue as a FilterRangeValue1 +func (t FilterRangeValue) AsFilterRangeValue1() (FilterRangeValue1, error) { + var body FilterRangeValue1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterRangeValue1 overwrites any union data inside the FilterRangeValue as the provided FilterRangeValue1 +func (t *FilterRangeValue) FromFilterRangeValue1(v FilterRangeValue1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterRangeValue1 performs a merge with any union data inside the FilterRangeValue, using the provided FilterRangeValue1 +func (t *FilterRangeValue) MergeFilterRangeValue1(v FilterRangeValue1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t FilterRangeValue) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FilterRangeValue) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsFilterValue0 returns the union data inside the FilterValue as a FilterValue0 +func (t FilterValue) AsFilterValue0() (FilterValue0, error) { + var body FilterValue0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterValue0 overwrites any union data inside the FilterValue as the provided FilterValue0 +func (t *FilterValue) FromFilterValue0(v FilterValue0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterValue0 performs a merge with any union data inside the FilterValue, using the provided FilterValue0 +func (t *FilterValue) MergeFilterValue0(v FilterValue0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterValue1 returns the union data inside the FilterValue as a FilterValue1 +func (t FilterValue) AsFilterValue1() (FilterValue1, error) { + var body FilterValue1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterValue1 overwrites any union data inside the FilterValue as the provided FilterValue1 +func (t *FilterValue) FromFilterValue1(v FilterValue1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterValue1 performs a merge with any union data inside the FilterValue, using the provided FilterValue1 +func (t *FilterValue) MergeFilterValue1(v FilterValue1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +// AsFilterValue2 returns the union data inside the FilterValue as a FilterValue2 +func (t FilterValue) AsFilterValue2() (FilterValue2, error) { + var body FilterValue2 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromFilterValue2 overwrites any union data inside the FilterValue as the provided FilterValue2 +func (t *FilterValue) FromFilterValue2(v FilterValue2) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeFilterValue2 performs a merge with any union data inside the FilterValue, using the provided FilterValue2 +func (t *FilterValue) MergeFilterValue2(v FilterValue2) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JsonMerge(t.union, b) + t.union = merged + return err +} + +func (t FilterValue) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FilterValue) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} diff --git a/internal/test/issues/issue-936/gen.go b/internal/test/issues/issue-936/gen.go new file mode 100644 index 0000000000..e21b0500de --- /dev/null +++ b/internal/test/issues/issue-936/gen.go @@ -0,0 +1,3 @@ +package issue936 + +//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.config.yaml spec.yaml diff --git a/internal/test/issues/issue-936/server.config.yaml b/internal/test/issues/issue-936/server.config.yaml new file mode 100644 index 0000000000..aa459d00dd --- /dev/null +++ b/internal/test/issues/issue-936/server.config.yaml @@ -0,0 +1,6 @@ +package: issue936 +output: api.gen.go +generate: + models: true +compatibility: + circular-reference-limit: 4 diff --git a/internal/test/issues/issue-936/spec.yaml b/internal/test/issues/issue-936/spec.yaml new file mode 100644 index 0000000000..8d9a7cf1d8 --- /dev/null +++ b/internal/test/issues/issue-936/spec.yaml @@ -0,0 +1,60 @@ +# Via https://github.com/getkin/kin-openapi/blob/b31a4bb2dcd523b1477da8854f0f75859cd314b3/openapi3/testdata/recursiveRef/issue615.yml +openapi: "3.0.3" +info: + title: Deep recursive cyclic refs example + version: "1.0" +paths: + /foo: {} +components: + schemas: + FilterColumnIncludes: + type: object + properties: + $includes: + $ref: '#/components/schemas/FilterPredicate' + additionalProperties: false + maxProperties: 1 + minProperties: 1 + FilterPredicate: + oneOf: + - $ref: '#/components/schemas/FilterValue' + - type: array + items: + $ref: '#/components/schemas/FilterPredicate' + minLength: 1 + - $ref: '#/components/schemas/FilterPredicateOp' + - $ref: '#/components/schemas/FilterPredicateRangeOp' + FilterPredicateOp: + type: object + properties: + $any: + oneOf: + - type: array + items: + $ref: '#/components/schemas/FilterPredicate' + $none: + oneOf: + - $ref: '#/components/schemas/FilterPredicate' + - type: array + items: + $ref: '#/components/schemas/FilterPredicate' + additionalProperties: false + maxProperties: 1 + minProperties: 1 + FilterPredicateRangeOp: + type: object + properties: + $lt: + $ref: '#/components/schemas/FilterRangeValue' + additionalProperties: false + maxProperties: 2 + minProperties: 2 + FilterRangeValue: + oneOf: + - type: number + - type: string + FilterValue: + oneOf: + - type: number + - type: string + - type: boolean diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index 9b64bfe25f..d26661762b 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -75,6 +75,11 @@ type CompatibilityOptions struct { // This resolves the behavior such that middlewares are chained in the order they are invoked. // Please see https://github.com/deepmap/oapi-codegen/issues/841 ApplyGorillaMiddlewareFirstToLast bool `yaml:"apply-gorilla-middleware-first-to-last,omitempty"` + // CircularReferenceLimit allows controlling the limit for circular reference checking. + // In some OpenAPI specifications, we have a higher number of circular + // references than is allowed out-of-the-box, but can be tuned to allow + // traversing them. + CircularReferenceLimit int `yaml:"circular-reference-limit"` } // OutputOptions are used to modify the output code in some way. diff --git a/pkg/util/loader.go b/pkg/util/loader.go index 9efbb8f38e..c3ab6d2865 100644 --- a/pkg/util/loader.go +++ b/pkg/util/loader.go @@ -18,3 +18,20 @@ func LoadSwagger(filePath string) (swagger *openapi3.T, err error) { return loader.LoadFromFile(filePath) } } + +func LoadSwaggerWithCircularReferenceCount(filePath string, circularReferenceCount int) (swagger *openapi3.T, err error) { + // get a copy of the existing count + existingCircularReferenceCount := openapi3.CircularReferenceCounter + if circularReferenceCount > 0 { + openapi3.CircularReferenceCounter = circularReferenceCount + } + + swagger, err = LoadSwagger(filePath) + + if circularReferenceCount > 0 { + // and make sure to reset it + openapi3.CircularReferenceCounter = existingCircularReferenceCount + } + + return swagger, err +}