Skip to content

Commit

Permalink
Move openapi3 visitedComponent from context to doc *T
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitriy Lukiyanchuk committed Aug 10, 2022
1 parent b02ecab commit 9b7b89b
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 53 deletions.
6 changes: 3 additions & 3 deletions openapi3/internalize_refs.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver)
}

func (doc *T) derefSchema(ctx context.Context, s *Schema, refNameResolver RefNameResolver) {
if s == nil || isVisitedSchema(ctx, s) {
if s == nil || doc.isVisitedSchema(ctx, s) {
return
}

Expand Down Expand Up @@ -230,7 +230,7 @@ func (doc *T) derefSchema(ctx context.Context, s *Schema, refNameResolver RefNam
func (doc *T) derefHeaders(ctx context.Context, hs Headers, refNameResolver RefNameResolver) {
for _, h := range hs {
doc.addHeaderToSpec(h, refNameResolver)
if isVisitedHeader(ctx, h.Value) {
if doc.isVisitedHeader(ctx, h.Value) {
continue
}
doc.derefParameter(ctx, h.Value.Parameter, refNameResolver)
Expand Down Expand Up @@ -328,7 +328,7 @@ func (doc *T) derefPaths(ctx context.Context, paths map[string]*PathItem, refNam
//
// doc.InternalizeRefs(context.Background(), nil)
func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref string) string) {
ctx = newVisited().withContext(ctx)
doc.resetVisited()

if refNameResolver == nil {
refNameResolver = DefaultRefNameResolver
Expand Down
2 changes: 2 additions & 0 deletions openapi3/openapi3.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type T struct {
Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"`
Tags Tags `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`

visited visitedComponent
}

// MarshalJSON returns the JSON encoding of T.
Expand Down
62 changes: 12 additions & 50 deletions openapi3/visited.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package openapi3

import "context"

func newVisited() *visitedComponent {
return &visitedComponent{
func newVisited() visitedComponent {
return visitedComponent{
header: make(map[*Header]struct{}),
schema: make(map[*Schema]struct{}),
}
Expand All @@ -14,68 +14,30 @@ type visitedComponent struct {
schema map[*Schema]struct{}
}

type ctxKey struct{}

// withContext returns a copy of ctx with visitedComponent associated. If an instance
// of visitedComponent is already in the context, the context is not updated.
//
// Returned ctx can be passed to children function calls and used to check whether
// component already visited. For instance:
//
// ctx := context.Background()
// ctx = newVisited().withContext(ctx)
// ...
// doc.deferSchemaRecursively(ctx, schema)
// func (doc *T) deferSchemaRecursively(ctx context.Context, s *Schema) {
// if s == nil || isVisitedSchema(ctx, s) {
// return
// }
// }
func (v *visitedComponent) withContext(ctx context.Context) context.Context {
if visited, ok := ctx.Value(ctxKey{}).(*visitedComponent); ok {
if visited == v {
// Do not store the same object.
return ctx
}
}

return context.WithValue(ctx, ctxKey{}, v)
}

// visitedCtx returns the visitedComponent associated with the ctx. If no one
// is associated, a new visitedComponent is returned.
//
// The ctx should be initialized with method `withContext` first.
func visitedCtx(ctx context.Context) *visitedComponent {
if v, ok := ctx.Value(ctxKey{}).(*visitedComponent); ok {
return v
}

return newVisited()
// resetVisited clears visitedComponent map
// should be called before recursion over doc *T
func (doc *T) resetVisited() {
doc.visited = newVisited()
}

// isVisitedHeader returns `true` if the *Header pointer was already visited
// otherwise it returns `false`
func isVisitedHeader(ctx context.Context, h *Header) bool {
visited := visitedCtx(ctx)

if _, ok := visited.header[h]; ok {
func (doc *T) isVisitedHeader(ctx context.Context, h *Header) bool {
if _, ok := doc.visited.header[h]; ok {
return true
}

visited.header[h] = struct{}{}
doc.visited.header[h] = struct{}{}
return false
}

// isVisitedHeader returns `true` if the *Schema pointer was already visited
// otherwise it returns `false`
func isVisitedSchema(ctx context.Context, s *Schema) bool {
visited := visitedCtx(ctx)

if _, ok := visited.schema[s]; ok {
func (doc *T) isVisitedSchema(ctx context.Context, s *Schema) bool {
if _, ok := doc.visited.schema[s]; ok {
return true
}

visited.schema[s] = struct{}{}
doc.visited.schema[s] = struct{}{}
return false
}

0 comments on commit 9b7b89b

Please sign in to comment.