Skip to content

Commit c789944

Browse files
reversearrowdaveshanley
authored andcommitted
feat: flag to allow converting from yaml to json pre validation
1 parent 6cb7222 commit c789944

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.7
55
require (
66
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad
77
github.com/dlclark/regexp2 v1.11.5
8+
github.com/goccy/go-yaml v1.18.0
89
github.com/pb33f/jsonpath v0.1.2
910
github.com/pb33f/libopenapi v0.28.2
1011
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
1111
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1212
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
1313
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
14+
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
15+
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
1416
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1517
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
1618
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=

0 commit comments

Comments
 (0)