Skip to content
Open
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
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ go get github.com/pb33f/libopenapi-validator
## Validate OpenAPI Document

```bash
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest [--regexengine] <file>
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest [--regexengine] [--yaml2json] <file>
```

### Options

#### --regexengine
🔍 Example: Use a custom regex engine/flag (e.g., ecmascript)
```bash
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --regexengine=ecmascript <file>
Expand All @@ -51,6 +55,26 @@ go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --regexengine=e
- re2
- unicode

#### --yaml2json
🔍 Convert YAML files to JSON before validation (ℹ️ Default: false)

[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.
These structures cannot be properly converted to JSON by libopenapi and cannot be validated by jsonschema, resulting in ambiguous errors.

This flag allows pre-converting from YAML to JSON to bypass this limitation of the libopenapi.

**When does this happen?**
- OpenAPI specs with deeply nested schema definitions
- Complex `allOf`, `oneOf`, or `anyOf` structures with multiple levels
- Specifications with intricate object mappings in examples or schema properties

Enabling this flag pre-converts the YAML document from YAML to JSON, ensuring a clean JSON structure before validation.

Example:
```bash
go run github.com/pb33f/libopenapi-validator/cmd/validate@latest --yaml2json <file>
```

## Documentation

- [The structure of the validator](https://pb33f.io/libopenapi/validation/#the-structure-of-the-validator)
Expand Down
41 changes: 37 additions & 4 deletions cmd/validate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"

"github.com/dlclark/regexp2"
"github.com/goccy/go-yaml"
"github.com/pb33f/libopenapi"
"github.com/santhosh-tekuri/jsonschema/v6"

Expand Down Expand Up @@ -64,13 +65,19 @@ var (
If not specified, the default libopenapi option is "re2".

If not specified, the default libopenapi regex engine is "re2"".`)
convertYAMLToJSON = flag.Bool("yaml2json", false, `Convert YAML files to JSON before validation.
libopenapi passes map[interface{}]interface{} structures for deeply nested objects
or complex mappings, which are not allowed in JSON and cannot be validated by jsonschema.
This flag allows pre-converting from YAML to JSON to bypass this limitation of the libopenapi.
Default is false.`)
)

// main is the entry point for validating an OpenAPI Specification (OAS) document.
// It uses the libopenapi-validator library to check if the provided OAS document
// conforms to the OpenAPI specification.
//
// This tool accepts a single input file (YAML or JSON) and provides an optional
// This tool accepts a single input file (YAML or JSON) and provides optional flags:
//
// `--regexengine` flag to customize the regex engine used during validation.
// This is useful for cases where the spec uses regex patterns that require engines
// like ECMAScript or RE2.
Expand All @@ -80,9 +87,16 @@ If not specified, the default libopenapi regex engine is "re2"".`)
// - Flags: ignorecase, multiline, explicitcapture, compiled, singleline,
// ignorepatternwhitespace, righttoleft, debug, unicode
//
// `--yaml2json` flag to convert YAML files to JSON before validation.
// libopenapi passes map[interface{}]interface{} structures for deeply nested
// objects or complex mappings, which are not allowed in JSON and cannot be
// validated by jsonschema. This flag allows pre-converting from YAML to JSON
// to bypass this limitation of the libopenapi. Default is false.
//
// Example usage:
//
// go run main.go --regexengine=ecmascript ./my-api-spec.yaml
// go run main.go --yaml2json ./my-api-spec.yaml
//
// If validation passes, the tool logs a success message.
// If the document is invalid or there is a processing error, it logs details and exits non-zero.
Expand All @@ -94,13 +108,21 @@ Validates an OpenAPI document using libopenapi-validator.

Options:
--regexengine string Specify the regex parsing option to use.
Supported values are:
Supported values are:
Engines: re2 (default), ecmascript
Flags: ignorecase, multiline, explicitcapture, compiled,
singleline, ignorepatternwhitespace, righttoleft,
Flags: ignorecase, multiline, explicitcapture, compiled,
singleline, ignorepatternwhitespace, righttoleft,
debug, unicode
If not specified, the default libopenapi option is "re2".

--yaml2json Convert YAML files to JSON before validation.
libopenapi passes map[interface{}]interface{}
structures for deeply nested objects or complex mappings, which
are not allowed in JSON and cannot be validated by jsonschema.
This flag allows pre-converting from YAML to JSON to bypass this
limitation of the libopenapi.
(default: false)

-h, --help Show this help message and exit.
`)
}
Expand Down Expand Up @@ -156,6 +178,17 @@ Options:
os.Exit(1)
}

if *convertYAMLToJSON {
var v interface{}
if err := yaml.Unmarshal(data, &v); err == nil {
data, err = yaml.YAMLToJSON(data)
if err != nil {
logger.Error("invalid api spec: error converting yaml to json", slog.Any("error", err))
os.Exit(1)
}
}
}

doc, err := libopenapi.NewDocument(data)
if err != nil {
logger.Error("error creating new libopenapi document", slog.Any("error", err))
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module github.com/pb33f/libopenapi-validator
go 1.24.7

require (
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad
github.com/dlclark/regexp2 v1.11.5
github.com/goccy/go-yaml v1.18.0
github.com/pb33f/jsonpath v0.1.2
github.com/pb33f/libopenapi v0.28.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
Expand All @@ -14,7 +16,6 @@ require (

require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pb33f/ordered-map/v2 v2.3.0 // indirect
Expand Down
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad h1:3swAvbzgfaI6nKuDDU7BiKfZRdF+h2ZwKgMHd8Ha4t8=
github.com/basgys/goxml2json v1.1.1-0.20231018121955-e66ee54ceaad/go.mod h1:9+nBLYNWkvPcq9ep0owWUsPTLgL9ZXTsZWcCSVGGLJ0=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
Expand All @@ -12,6 +11,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down