Repository: ogen-go/ogen
Pull request #551 (2022-09-05 06:52:49)

fix(parser): use `RefKey` as cache key for components


Patch:
diff --git a/openapi/parser/parse_security.go b/openapi/parser/parse_security.go
index e4e771d4..ca9176ff 100644
--- a/openapi/parser/parse_security.go
+++ b/openapi/parser/parse_security.go
@@ -190,7 +190,7 @@ func (p *parser) parseSecurityRequirements(
 		locator := locator.Index(idx)
 
 		for name, scopes := range req {
-			v, ok := p.refs.securitySchemes[name]
+			v, ok := p.securitySchemes[name]
 			if !ok {
 				err := errors.Errorf("unknown security schema %q", name)
 				return nil, p.wrapLocation(ctx.LastLoc(), locator.Key(name), err)
diff --git a/openapi/parser/parser.go b/openapi/parser/parser.go
index d44dbce4..3d98ade6 100644
--- a/openapi/parser/parser.go
+++ b/openapi/parser/parser.go
@@ -14,6 +14,8 @@ import (
 	"github.com/ogen-go/ogen/openapi"
 )
 
+type refKey = jsonpointer.RefKey
+
 type parser struct {
 	// api spec, immutable.
 	spec *ogen.Spec
@@ -23,14 +25,16 @@ type parser struct {
 	operations []*openapi.Operation
 	// refs contains lazy-initialized referenced components.
 	refs struct {
-		requestBodies   map[string]*openapi.RequestBody
-		responses       map[string]*openapi.Response
-		parameters      map[string]*openapi.Parameter
-		headers         map[string]*openapi.Header
-		examples        map[string]*openapi.Example
-		securitySchemes map[string]*ogen.SecurityScheme
-		pathItems       map[string]pathItem
+		requestBodies   map[refKey]*openapi.RequestBody
+		responses       map[refKey]*openapi.Response
+		parameters      map[refKey]*openapi.Parameter
+		headers         map[refKey]*openapi.Header
+		examples        map[refKey]*openapi.Example
+		securitySchemes map[refKey]*ogen.SecurityScheme
+		pathItems       map[refKey]pathItem
 	}
+	// securitySchemes contains security schemes defined in the root spec.
+	securitySchemes map[string]*ogen.SecurityScheme
 
 	external   jsonschema.ExternalResolver
 	schemas    map[string]*yaml.Node
@@ -52,23 +56,24 @@ func Parse(spec *ogen.Spec, s Settings) (*openapi.API, error) {
 		spec:       spec,
 		operations: nil,
 		refs: struct {
-			requestBodies   map[string]*openapi.RequestBody
-			responses       map[string]*openapi.Response
-			parameters      map[string]*openapi.Parameter
-			headers         map[string]*openapi.Header
-			examples        map[string]*openapi.Example
-			securitySchemes map[string]*ogen.SecurityScheme
-			pathItems       map[string]pathItem
+			requestBodies   map[refKey]*openapi.RequestBody
+			responses       map[refKey]*openapi.Response
+			parameters      map[refKey]*openapi.Parameter
+			headers         map[refKey]*openapi.Header
+			examples        map[refKey]*openapi.Example
+			securitySchemes map[refKey]*ogen.SecurityScheme
+			pathItems       map[refKey]pathItem
 		}{
-			requestBodies:   map[string]*openapi.RequestBody{},
-			responses:       map[string]*openapi.Response{},
-			parameters:      map[string]*openapi.Parameter{},
-			headers:         map[string]*openapi.Header{},
-			examples:        map[string]*openapi.Example{},
-			securitySchemes: map[string]*ogen.SecurityScheme{},
-			pathItems:       map[string]pathItem{},
+			requestBodies:   map[refKey]*openapi.RequestBody{},
+			responses:       map[refKey]*openapi.Response{},
+			parameters:      map[refKey]*openapi.Parameter{},
+			headers:         map[refKey]*openapi.Header{},
+			examples:        map[refKey]*openapi.Example{},
+			securitySchemes: map[refKey]*ogen.SecurityScheme{},
+			pathItems:       map[refKey]pathItem{},
 		},
-		external: s.External,
+		securitySchemes: map[string]*ogen.SecurityScheme{},
+		external:        s.External,
 		schemas: map[string]*yaml.Node{
 			"": spec.Raw,
 		},
@@ -91,7 +96,7 @@ func Parse(spec *ogen.Spec, s Settings) (*openapi.API, error) {
 	}
 
 	for name, s := range spec.Components.SecuritySchemes {
-		p.refs.securitySchemes[name] = s
+		p.securitySchemes[name] = s
 	}
 
 	components, err := p.parseComponents(spec.Components)
diff --git a/openapi/parser/resolve.go b/openapi/parser/resolve.go
index f3b580a5..92f7344a 100644
--- a/openapi/parser/resolve.go
+++ b/openapi/parser/resolve.go
@@ -59,12 +59,16 @@ func (p *parser) resolve(key jsonpointer.RefKey, ctx *jsonpointer.ResolveCtx, to
 func (p *parser) resolveRequestBody(ref string, ctx *jsonpointer.ResolveCtx) (*openapi.RequestBody, error) {
 	const prefix = "#/components/requestBodies/"
 
-	if r, ok := p.refs.requestBodies[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if r, ok := p.refs.requestBodies[key]; ok {
 		return r, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -94,19 +98,23 @@ func (p *parser) resolveRequestBody(ref string, ctx *jsonpointer.ResolveCtx) (*o
 	}
 
 	r.Ref = ref
-	p.refs.requestBodies[ref] = r
+	p.refs.requestBodies[key] = r
 	return r, nil
 }
 
 func (p *parser) resolveResponse(ref string, ctx *jsonpointer.ResolveCtx) (*openapi.Response, error) {
 	const prefix = "#/components/responses/"
 
-	if r, ok := p.refs.responses[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if r, ok := p.refs.responses[key]; ok {
 		return r, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -136,19 +144,23 @@ func (p *parser) resolveResponse(ref string, ctx *jsonpointer.ResolveCtx) (*open
 	}
 
 	r.Ref = ref
-	p.refs.responses[ref] = r
+	p.refs.responses[key] = r
 	return r, nil
 }
 
 func (p *parser) resolveParameter(ref string, ctx *jsonpointer.ResolveCtx) (*openapi.Parameter, error) {
 	const prefix = "#/components/parameters/"
 
-	if param, ok := p.refs.parameters[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if param, ok := p.refs.parameters[key]; ok {
 		return param, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -178,19 +190,23 @@ func (p *parser) resolveParameter(ref string, ctx *jsonpointer.ResolveCtx) (*ope
 	}
 
 	param.Ref = ref
-	p.refs.parameters[ref] = param
+	p.refs.parameters[key] = param
 	return param, nil
 }
 
 func (p *parser) resolveHeader(headerName, ref string, ctx *jsonpointer.ResolveCtx) (*openapi.Header, error) {
 	const prefix = "#/components/headers/"
 
-	if header, ok := p.refs.headers[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if header, ok := p.refs.headers[key]; ok {
 		return header, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -220,19 +236,23 @@ func (p *parser) resolveHeader(headerName, ref string, ctx *jsonpointer.ResolveC
 	}
 
 	header.Ref = ref
-	p.refs.headers[ref] = header
+	p.refs.headers[key] = header
 	return header, nil
 }
 
 func (p *parser) resolveExample(ref string, ctx *jsonpointer.ResolveCtx) (*openapi.Example, error) {
 	const prefix = "#/components/examples/"
 
-	if ex, ok := p.refs.examples[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if ex, ok := p.refs.examples[key]; ok {
 		return ex, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -264,19 +284,23 @@ func (p *parser) resolveExample(ref string, ctx *jsonpointer.ResolveCtx) (*opena
 	if ex != nil {
 		ex.Ref = ref
 	}
-	p.refs.examples[ref] = ex
+	p.refs.examples[key] = ex
 	return ex, nil
 }
 
 func (p *parser) resolveSecurityScheme(ref string, ctx *jsonpointer.ResolveCtx) (*ogen.SecurityScheme, error) {
 	const prefix = "#/components/securitySchemes/"
 
-	if r, ok := p.refs.securitySchemes[ref]; ok {
-		return r, nil
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if s, ok := p.refs.securitySchemes[key]; ok {
+		return s, nil
+	}
+
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -305,7 +329,7 @@ func (p *parser) resolveSecurityScheme(ref string, ctx *jsonpointer.ResolveCtx)
 		return nil, err
 	}
 
-	p.refs.securitySchemes[ref] = ss
+	p.refs.securitySchemes[key] = ss
 	return ss, nil
 }
 
@@ -316,12 +340,16 @@ func (p *parser) resolvePathItem(
 ) (pathItem, error) {
 	const prefix = "#/components/pathItems/"
 
-	if r, ok := p.refs.pathItems[ref]; ok {
+	key, err := ctx.Key(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	if r, ok := p.refs.pathItems[key]; ok {
 		return r, nil
 	}
 
-	key, err := ctx.Add(ref)
-	if err != nil {
+	if err := ctx.AddKey(key); err != nil {
 		return nil, err
 	}
 	defer func() {
@@ -350,6 +378,6 @@ func (p *parser) resolvePathItem(
 		return nil, err
 	}
 
-	p.refs.pathItems[ref] = r
+	p.refs.pathItems[key] = r
 	return r, nil
 }


Test patch:
diff --git a/openapi/parser/resolve_external_test.go b/openapi/parser/resolve_external_test.go
index 9c534d8a..9ce039ca 100644
--- a/openapi/parser/resolve_external_test.go
+++ b/openapi/parser/resolve_external_test.go
@@ -223,7 +223,7 @@ func TestExternalReference(t *testing.T) {
 					Schema: schema,
 					Examples: map[string]*openapi.Example{
 						"ref": {
-							Ref:   "#/components/examples/RemoteExample",
+							Ref:   "#/components/examples/LocalExample",
 							Value: exampleValue,
 						},
 					},