Skip to content

Commit 3074e7d

Browse files
author
Michael Bonifacio
committed
Merge origin/main into unify-context-and-centralize
Resolves conflicts in go.mod and go.sum by taking newer versions: - go.yaml.in/yaml/v4 v4.0.0-rc.3 - golang.org/x/text v0.32.0 - golang.org/x/net v0.48.0
2 parents d750797 + 3512bfa commit 3074e7d

12 files changed

Lines changed: 234 additions & 35 deletions

File tree

.github/workflows/build.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ jobs:
2424
- name: golangci-lint
2525
uses: golangci/golangci-lint-action@v8
2626
with:
27-
version: v2.1
27+
version: v2.7
2828
build:
2929
name: Build
3030
runs-on: ubuntu-latest
3131
steps:
3232
- name: Set up Go 1.x
3333
uses: actions/setup-go@v3
3434
with:
35-
go-version: 1.24.7
35+
go-version: 1.25
3636
id: go
3737

3838
- name: Checkout code

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ go get github.com/pb33f/libopenapi-validator
3131
## Validate OpenAPI Document
3232

3333
```bash
34-
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest [--regexengine] <file>
34+
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest [--regexengine] [--yaml2json] <file>
3535
```
36+
37+
### Options
38+
39+
#### --regexengine
3640
🔍 Example: Use a custom regex engine/flag (e.g., ecmascript)
3741
```bash
3842
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --regexengine=ecmascript <file>
@@ -51,6 +55,26 @@ go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --regexengine=e
5155
- re2
5256
- unicode
5357

58+
#### --yaml2json
59+
🔍 Convert YAML files to JSON before validation (ℹ️ Default: false)
60+
61+
[libopenapi](https://github.com/pb33f/libopenapi/blob/main/datamodel/spec_info.go#L115) passes `map[interface{}]interface{}` structures for deeply nested objects or complex mappings in the OpenAPI specification, which are not allowed in JSON.
62+
These structures cannot be properly converted to JSON by libopenapi and cannot be validated by jsonschema, resulting in ambiguous errors.
63+
64+
This flag allows pre-converting from YAML to JSON to bypass this limitation of the libopenapi.
65+
66+
**When does this happen?**
67+
- OpenAPI specs with deeply nested schema definitions
68+
- Complex `allOf`, `oneOf`, or `anyOf` structures with multiple levels
69+
- Specifications with intricate object mappings in examples or schema properties
70+
71+
Enabling this flag pre-converts the YAML document from YAML to JSON, ensuring a clean JSON structure before validation.
72+
73+
Example:
74+
```bash
75+
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --yaml2json <file>
76+
```
77+
5478
## Documentation
5579

5680
- [The structure of the validator](https://pb33f.io/libopenapi/validation/#the-structure-of-the-validator)

cmd/validate/main.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99

1010
"github.com/dlclark/regexp2"
11+
"github.com/goccy/go-yaml"
1112
"github.com/pb33f/libopenapi"
1213
"github.com/santhosh-tekuri/jsonschema/v6"
1314

@@ -64,13 +65,19 @@ var (
6465
If not specified, the default libopenapi option is "re2".
6566
6667
If not specified, the default libopenapi regex engine is "re2"".`)
68+
convertYAMLToJSON = flag.Bool("yaml2json", false, `Convert YAML files to JSON before validation.
69+
libopenapi passes map[interface{}]interface{} structures for deeply nested objects
70+
or complex mappings, which are not allowed in JSON and cannot be validated by jsonschema.
71+
This flag allows pre-converting from YAML to JSON to bypass this limitation of the libopenapi.
72+
Default is false.`)
6773
)
6874

6975
// main is the entry point for validating an OpenAPI Specification (OAS) document.
7076
// It uses the libopenapi-validator library to check if the provided OAS document
7177
// conforms to the OpenAPI specification.
7278
//
73-
// This tool accepts a single input file (YAML or JSON) and provides an optional
79+
// This tool accepts a single input file (YAML or JSON) and provides optional flags:
80+
//
7481
// `--regexengine` flag to customize the regex engine used during validation.
7582
// This is useful for cases where the spec uses regex patterns that require engines
7683
// like ECMAScript or RE2.
@@ -80,9 +87,16 @@ If not specified, the default libopenapi regex engine is "re2"".`)
8087
// - Flags: ignorecase, multiline, explicitcapture, compiled, singleline,
8188
// ignorepatternwhitespace, righttoleft, debug, unicode
8289
//
90+
// `--yaml2json` flag to convert YAML files to JSON before validation.
91+
// libopenapi passes map[interface{}]interface{} structures for deeply nested
92+
// objects or complex mappings, which are not allowed in JSON and cannot be
93+
// validated by jsonschema. This flag allows pre-converting from YAML to JSON
94+
// to bypass this limitation of the libopenapi. Default is false.
95+
//
8396
// Example usage:
8497
//
8598
// go run main.go --regexengine=ecmascript ./my-api-spec.yaml
99+
// go run main.go --yaml2json ./my-api-spec.yaml
86100
//
87101
// If validation passes, the tool logs a success message.
88102
// If the document is invalid or there is a processing error, it logs details and exits non-zero.
@@ -94,13 +108,21 @@ Validates an OpenAPI document using libopenapi-validator.
94108
95109
Options:
96110
--regexengine string Specify the regex parsing option to use.
97-
Supported values are:
111+
Supported values are:
98112
Engines: re2 (default), ecmascript
99-
Flags: ignorecase, multiline, explicitcapture, compiled,
100-
singleline, ignorepatternwhitespace, righttoleft,
113+
Flags: ignorecase, multiline, explicitcapture, compiled,
114+
singleline, ignorepatternwhitespace, righttoleft,
101115
debug, unicode
102116
If not specified, the default libopenapi option is "re2".
103117
118+
--yaml2json Convert YAML files to JSON before validation.
119+
libopenapi passes map[interface{}]interface{}
120+
structures for deeply nested objects or complex mappings, which
121+
are not allowed in JSON and cannot be validated by jsonschema.
122+
This flag allows pre-converting from YAML to JSON to bypass this
123+
limitation of the libopenapi.
124+
(default: false)
125+
104126
-h, --help Show this help message and exit.
105127
`)
106128
}
@@ -156,6 +178,17 @@ Options:
156178
os.Exit(1)
157179
}
158180

181+
if *convertYAMLToJSON {
182+
var v interface{}
183+
if err := yaml.Unmarshal(data, &v); err == nil {
184+
data, err = yaml.YAMLToJSON(data)
185+
if err != nil {
186+
logger.Error("invalid api spec: error converting yaml to json", slog.Any("error", err))
187+
os.Exit(1)
188+
}
189+
}
190+
}
191+
159192
doc, err := libopenapi.NewDocument(data)
160193
if err != nil {
161194
logger.Error("error creating new libopenapi document", slog.Any("error", err))

go.mod

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
module github.com/pb33f/libopenapi-validator
22

3-
go 1.24.7
3+
go 1.25.0
44

55
require (
6+
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad
67
github.com/dlclark/regexp2 v1.11.5
7-
github.com/pb33f/jsonpath v0.1.2
8-
github.com/pb33f/libopenapi v0.28.1
8+
github.com/goccy/go-yaml v1.19.1
9+
github.com/pb33f/jsonpath v0.7.0
10+
github.com/pb33f/libopenapi v0.30.2
911
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
1012
github.com/stretchr/testify v1.11.1
11-
go.yaml.in/yaml/v4 v4.0.0-rc.2
12-
golang.org/x/text v0.31.0
13+
go.yaml.in/yaml/v4 v4.0.0-rc.3
14+
golang.org/x/text v0.32.0
1315
)
1416

1517
require (
1618
github.com/bahlo/generic-list-go v0.2.0 // indirect
17-
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad // indirect
1819
github.com/buger/jsonparser v1.1.1 // indirect
1920
github.com/davecgh/go-spew v1.1.1 // indirect
2021
github.com/pb33f/ordered-map/v2 v2.3.0 // indirect
2122
github.com/pmezard/go-difflib v1.0.0 // indirect
22-
golang.org/x/net v0.47.0 // indirect
23+
golang.org/x/net v0.48.0 // indirect
2324
gopkg.in/yaml.v3 v3.0.1 // indirect
2425
)

go.sum

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
22
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
3-
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
4-
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
53
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad h1:3swAvbzgfaI6nKuDDU7BiKfZRdF+h2ZwKgMHd8Ha4t8=
64
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad/go.mod h1:9+nBLYNWkvPcq9ep0owWUsPTLgL9ZXTsZWcCSVGGLJ0=
5+
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
76
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
87
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
98
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
@@ -12,14 +11,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
1211
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1312
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
1413
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
14+
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
15+
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
1516
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1617
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
1718
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1819
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
19-
github.com/pb33f/jsonpath v0.1.2 h1:PlqXjEyecMqoYJupLxYeClCGWEpAFnh4pmzgspbXDPI=
20-
github.com/pb33f/jsonpath v0.1.2/go.mod h1:TtKnUnfqZm48q7a56DxB3WtL3ipkVtukMKGKxaR/uXU=
21-
github.com/pb33f/libopenapi v0.28.1 h1:vqE1Q08F6ohABsyKcK8kX7HYkR/+sILXGwCgFzF+aOg=
22-
github.com/pb33f/libopenapi v0.28.1/go.mod h1:mHMHA3ZKSZDTInNAuUtqkHlKLIjPm2HN1vgsGR57afc=
20+
github.com/pb33f/jsonpath v0.7.0 h1:3oG6yu1RqNoMZpqnRjBMqi8fSIXWoDAKDrsB0QGTcoU=
21+
github.com/pb33f/jsonpath v0.7.0/go.mod h1:/+JlSIjWA2ijMVYGJ3IQPF4Q1nLMYbUTYNdk0exCDPQ=
22+
github.com/pb33f/libopenapi v0.30.2 h1:xOldKP2h5rnBs3Q1EsJULgcplGz2iEem7FybLX8TySU=
23+
github.com/pb33f/libopenapi v0.30.2/go.mod h1:4MP76dnaTMY+DM+bRhKBneAIhVISEEZM6G6sd7A9pus=
2324
github.com/pb33f/ordered-map/v2 v2.3.0 h1:k2OhVEQkhTCQMhAicQ3Z6iInzoZNQ7L9MVomwKBZ5WQ=
2425
github.com/pb33f/ordered-map/v2 v2.3.0/go.mod h1:oe5ue+6ZNhy7QN9cPZvPA23Hx0vMHnNVeMg4fGdCANw=
2526
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -33,8 +34,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
3334
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
3435
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
3536
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
36-
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
37-
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
37+
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
38+
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
3839
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3940
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
4041
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
@@ -46,8 +47,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
4647
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
4748
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
4849
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
49-
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
50-
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
50+
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
51+
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
5152
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
5253
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
5354
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -70,10 +71,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
7071
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
7172
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
7273
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
73-
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
74-
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
75-
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
76-
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
74+
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
75+
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
7776
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
7877
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
7978
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

parameters/validate_parameter.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ func ValidateParameterSchema(
9797
var validationErrors []*errors.ValidationError
9898

9999
// 1. build a JSON render of the schema.
100-
renderedSchema, _ := schema.RenderInline()
100+
renderCtx := base.NewInlineRenderContext()
101+
renderedSchema, _ := schema.RenderInlineWithContext(renderCtx)
101102
jsonSchema, _ := utils.ConvertYAMLtoJSON(renderedSchema)
102103

103104
// 2. decode the object into a json blob.
@@ -238,7 +239,8 @@ func formatJsonSchemaValidationError(schema *base.Schema, scErrs *jsonschema.Val
238239
OriginalJsonSchemaError: scErrs,
239240
}
240241
if schema != nil {
241-
rendered, err := schema.RenderInline()
242+
renderCtx := base.NewInlineRenderContext()
243+
rendered, err := schema.RenderInlineWithContext(renderCtx)
242244
if err == nil && rendered != nil {
243245
renderedBytes, _ := json.Marshal(rendered)
244246
fail.ReferenceSchema = string(renderedBytes)

requests/validate_request.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,34 @@ func ValidateRequestSchema(input *ValidateRequestSchemaInput) (bool, []*errors.V
7575

7676
// Cache miss or no cache - render and compile
7777
if compiledSchema == nil {
78-
renderedSchema, _ = input.Schema.RenderInline()
78+
renderCtx := base.NewInlineRenderContext()
79+
var renderErr error
80+
renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx)
7981
referenceSchema = string(renderedSchema)
82+
83+
// If rendering failed (e.g., circular reference), return the render error
84+
if renderErr != nil {
85+
violation := &errors.SchemaValidationFailure{
86+
Reason: renderErr.Error(),
87+
Location: "schema rendering",
88+
ReferenceSchema: referenceSchema,
89+
}
90+
validationErrors = append(validationErrors, &errors.ValidationError{
91+
ValidationType: helpers.RequestBodyValidation,
92+
ValidationSubType: helpers.Schema,
93+
Message: fmt.Sprintf("%s request body for '%s' failed schema rendering",
94+
input.Request.Method, input.Request.URL.Path),
95+
Reason: fmt.Sprintf("The request schema failed to render: %s",
96+
renderErr.Error()),
97+
SpecLine: 1,
98+
SpecCol: 0,
99+
SchemaValidationErrors: []*errors.SchemaValidationFailure{violation},
100+
HowToFix: "check the request schema for circular references or invalid structures",
101+
Context: referenceSchema,
102+
})
103+
return false, validationErrors
104+
}
105+
80106
jsonSchema, _ = utils.ConvertYAMLtoJSON(renderedSchema)
81107

82108
var err error

requests/validate_request_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,44 @@ func indentLines(s string, indent string) string {
234234
}
235235
return strings.Join(lines, "\n")
236236
}
237+
238+
func TestValidateRequestSchema_CircularReference(t *testing.T) {
239+
// Test when schema has a circular reference that causes render failure
240+
spec := `openapi: 3.1.0
241+
info:
242+
title: Test
243+
version: 1.0.0
244+
components:
245+
schemas:
246+
Error:
247+
type: object
248+
properties:
249+
code:
250+
type: string
251+
details:
252+
type: array
253+
items:
254+
$ref: '#/components/schemas/Error'`
255+
256+
doc, err := libopenapi.NewDocument([]byte(spec))
257+
require.NoError(t, err)
258+
model, errs := doc.BuildV3Model()
259+
require.Empty(t, errs)
260+
261+
// Verify circular reference was detected
262+
require.Len(t, model.Index.GetCircularReferences(), 1)
263+
264+
schema := model.Model.Components.Schemas.GetOrZero("Error")
265+
require.NotNil(t, schema)
266+
267+
valid, errors := ValidateRequestSchema(&ValidateRequestSchemaInput{
268+
Request: postRequestWithBody(`{"code": "abc", "details": [{"code": "def"}]}`),
269+
Schema: schema.Schema(),
270+
Version: 3.1,
271+
})
272+
273+
assert.False(t, valid)
274+
require.Len(t, errors, 1)
275+
assert.Contains(t, errors[0].Message, "failed schema rendering")
276+
assert.Contains(t, errors[0].Reason, "circular reference")
277+
}

responses/validate_response.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,34 @@ func ValidateResponseSchema(input *ValidateResponseSchemaInput) (bool, []*errors
7878

7979
// Cache miss or no cache - render and compile
8080
if compiledSchema == nil {
81-
renderedSchema, _ = input.Schema.RenderInline()
81+
renderCtx := base.NewInlineRenderContext()
82+
var renderErr error
83+
renderedSchema, renderErr = input.Schema.RenderInlineWithContext(renderCtx)
8284
referenceSchema = string(renderedSchema)
85+
86+
// If rendering failed (e.g., circular reference), return the render error
87+
if renderErr != nil {
88+
violation := &errors.SchemaValidationFailure{
89+
Reason: renderErr.Error(),
90+
Location: "schema rendering",
91+
ReferenceSchema: referenceSchema,
92+
}
93+
validationErrors = append(validationErrors, &errors.ValidationError{
94+
ValidationType: helpers.ResponseBodyValidation,
95+
ValidationSubType: helpers.Schema,
96+
Message: fmt.Sprintf("%d response body for '%s' failed schema rendering",
97+
input.Response.StatusCode, input.Request.URL.Path),
98+
Reason: fmt.Sprintf("The response schema for status code '%d' failed to render: %s",
99+
input.Response.StatusCode, renderErr.Error()),
100+
SpecLine: 1,
101+
SpecCol: 0,
102+
SchemaValidationErrors: []*errors.SchemaValidationFailure{violation},
103+
HowToFix: "check the response schema for circular references or invalid structures",
104+
Context: referenceSchema,
105+
})
106+
return false, validationErrors
107+
}
108+
83109
jsonSchema, _ = utils.ConvertYAMLtoJSON(renderedSchema)
84110

85111
var err error

0 commit comments

Comments
 (0)