Skip to content

Commit

Permalink
Merge pull request #560 from tdakkota/fix/validate-components-key
Browse files Browse the repository at this point in the history
fix(parser): validate component map key as spec says
  • Loading branch information
shadowspore authored Sep 11, 2022
2 parents 428f6c0 + 39c0dc9 commit f6f6b4d
Show file tree
Hide file tree
Showing 11 changed files with 347 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"callbacks": {
"foo!bar": {
"http://localhost:1000": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"examples": {
"foo!bar": {
"value": true
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"headers": {
"foo!bar": {
"schema": {
"type": "string"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"links": {
"foo!bar": {
"operationId": "foo"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"parameters": {
"foo!bar": {
"name": "foobar",
"in": "query",
"schema": {
"type": "string"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"pathItems": {
"foo!bar": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"requestBodies": {
"foo!bar": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"responses": {
"foo!bar": {
"description": "User info"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"schemas": {
"foo!bar": {
"type": "string"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"openapi": "3.1.0",
"info": {
"title": "title",
"version": "v0.1.0"
},
"paths": {
"/foo": {
"get": {
"responses": {
"200": {
"description": "User info"
}
}
}
}
},
"components": {
"securitySchemes": {
"foo!bar": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}
}
70 changes: 69 additions & 1 deletion openapi/parser/parse_components.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,78 @@
package parser

import (
"regexp"

"github.com/go-faster/errors"

"github.com/ogen-go/ogen"
"github.com/ogen-go/ogen/internal/jsonpointer"
"github.com/ogen-go/ogen/internal/location"
"github.com/ogen-go/ogen/jsonschema"
"github.com/ogen-go/ogen/openapi"
)

var componentsKeyRegex = regexp.MustCompile(`^[a-zA-Z0-9.\-_]+$`)

// validateComponentsKey validates components key.
//
// Spec says:
//
// All the fixed fields declared above are objects that MUST use keys that
// match the regular expression: ^[a-zA-Z0-9\.\-_]+$.
//
// See https://spec.openapis.org/oas/v3.1.0#components-object.
func validateComponentsKey[Object any](p *parser, m map[string]Object, locator location.Locator) error {
for name := range m {
if !componentsKeyRegex.MatchString(name) {
locator := locator.Key(name)
err := errors.Errorf("invalid name: %q doesn't match %q", name, componentsKeyRegex)
return p.wrapLocation("", locator, err)
}
}
return nil
}

// validateComponentsKeys validates components keys.
//
// See validateComponentsKey comment.
func validateComponentsKeys(p *parser, c *ogen.Components) error {
if c == nil {
return nil
}
if err := validateComponentsKey(p, c.Schemas, c.Locator.Field("schemas")); err != nil {
return errors.Wrap(err, "schemas")
}
if err := validateComponentsKey(p, c.Responses, c.Locator.Field("responses")); err != nil {
return errors.Wrap(err, "responses")
}
if err := validateComponentsKey(p, c.Parameters, c.Locator.Field("parameters")); err != nil {
return errors.Wrap(err, "parameters")
}
if err := validateComponentsKey(p, c.Examples, c.Locator.Field("examples")); err != nil {
return errors.Wrap(err, "examples")
}
if err := validateComponentsKey(p, c.RequestBodies, c.Locator.Field("requestBodies")); err != nil {
return errors.Wrap(err, "requestBodies")
}
if err := validateComponentsKey(p, c.Headers, c.Locator.Field("headers")); err != nil {
return errors.Wrap(err, "headers")
}
if err := validateComponentsKey(p, c.SecuritySchemes, c.Locator.Field("securitySchemes")); err != nil {
return errors.Wrap(err, "securitySchemes")
}
if err := validateComponentsKey(p, c.Links, c.Locator.Field("links")); err != nil {
return errors.Wrap(err, "links")
}
if err := validateComponentsKey(p, c.Callbacks, c.Locator.Field("callbacks")); err != nil {
return errors.Wrap(err, "callbacks")
}
if err := validateComponentsKey(p, c.PathItems, c.Locator.Field("pathItems")); err != nil {
return errors.Wrap(err, "pathItems")
}
return nil
}

func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rerr error) {
if c == nil {
return &openapi.Components{
Expand All @@ -23,6 +87,10 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
rerr = p.wrapLocation("", c.Locator, rerr)
}()

if err := validateComponentsKeys(p, c); err != nil {
return nil, err
}

result := &openapi.Components{
Schemas: make(map[string]*jsonschema.Schema, len(c.Schemas)),
Responses: make(map[string]*openapi.Response, len(c.Responses)),
Expand All @@ -32,7 +100,7 @@ func (p *parser) parseComponents(c *ogen.Components) (_ *openapi.Components, rer
}
wrapErr := func(component, name string, err error) error {
loc := c.Locator.Field(component).Field(name)
err = errors.Wrapf(err, "schemas: %q", name)
err = errors.Wrapf(err, "%s: %q", component, name)
return p.wrapLocation("", loc, err)
}

Expand Down

0 comments on commit f6f6b4d

Please sign in to comment.