walker

package
v1.38.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package walker provides a document traversal API for OpenAPI specifications.

The walker enables single-pass traversal of OAS 2.0, 3.0.x, 3.1.x, and 3.2.0 documents, allowing handlers to receive and optionally mutate nodes. This is useful for analysis, transformation, and validation tasks that need to inspect or modify multiple parts of a specification in a consistent way.

Quick Start

Walk a document and collect all operation IDs:

result, _ := parser.ParseWithOptions(parser.WithFilePath("api.yaml"))

var operationIDs []string
err := walker.Walk(result,
    walker.WithOperationHandler(func(wc *walker.WalkContext, op *parser.Operation) walker.Action {
        operationIDs = append(operationIDs, op.OperationID)
        return walker.Continue
    }),
)

Flow Control

Handlers return an Action to control traversal:

  • Continue: continue traversing children and siblings normally
  • SkipChildren: skip all children of the current node, continue with siblings
  • Stop: stop the entire walk immediately

Example using SkipChildren to avoid internal paths:

walker.Walk(result,
    walker.WithPathHandler(func(wc *walker.WalkContext, pathItem *parser.PathItem) walker.Action {
        if strings.HasPrefix(wc.PathTemplate, "/internal") {
            return walker.SkipChildren
        }
        return walker.Continue
    }),
)

Handler Types

The walker provides typed handlers for all major OAS node types:

Mutation Support

Handlers receive pointers to the actual nodes, so mutations are applied directly:

walker.Walk(result,
    walker.WithSchemaHandler(func(wc *walker.WalkContext, schema *parser.Schema) walker.Action {
        if schema.Extra == nil {
            schema.Extra = make(map[string]any)
        }
        schema.Extra["x-processed"] = true
        return walker.Continue
    }),
)

WalkContext

Every handler receives a WalkContext as its first parameter, providing contextual information about the current node:

  • JSONPath: Full JSON path to the node (always populated)
  • PathTemplate: URL path template when in $.paths scope
  • Method: HTTP method when in operation scope (e.g., "get", "post")
  • StatusCode: Status code when in response scope (e.g., "200", "default")
  • Name: Map key for named items (headers, schemas, etc.)
  • IsComponent: True when in components/definitions section

Example JSON paths:

$.info                              // Info object
$.paths['/pets/{petId}']            // Path entry
$.paths['/pets'].get                // Operation
$.components.schemas['Pet']         // Schema

Use helper methods like WalkContext.InOperationScope and WalkContext.InResponseScope for scope checks.

Context Propagation

Pass a context.Context for cancellation and timeout support:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

walker.Walk(result,
    walker.WithContext(ctx),
    walker.WithSchemaHandler(func(wc *walker.WalkContext, schema *parser.Schema) walker.Action {
        if wc.Context().Err() != nil {
            return walker.Stop
        }
        return walker.Continue
    }),
)

Performance Considerations

The walker uses the Parse-Once pattern. Always prefer passing a pre-parsed parser.ParseResult rather than re-parsing:

// Good: parse once, walk multiple times
result, _ := parser.ParseWithOptions(parser.WithFilePath("api.yaml"))
walker.Walk(result, handlers1...)
walker.Walk(result, handlers2...)

Schema Cycle Detection

The walker automatically detects circular schema references and avoids infinite loops. Use WithMaxSchemaDepth to limit recursion depth for deeply nested schemas (default: 100).

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Walk

func Walk(result *parser.ParseResult, opts ...Option) error

Walk traverses the parsed document and calls registered handlers for each node.

Example
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info: &parser.Info{
			Title:   "Pet Store API",
			Version: "1.0.0",
		},
		Paths: parser.Paths{
			"/pets": &parser.PathItem{
				Get: &parser.Operation{
					OperationID: "listPets",
					Summary:     "List all pets",
				},
				Post: &parser.Operation{
					OperationID: "createPet",
					Summary:     "Create a pet",
				},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	var operationIDs []string
	_ = walker.Walk(result,
		walker.WithOperationHandler(func(wc *walker.WalkContext, op *parser.Operation) walker.Action {
			operationIDs = append(operationIDs, op.OperationID)
			return walker.Continue
		}),
	)

	for _, id := range operationIDs {
		fmt.Println(id)
	}
}
Output:

listPets
createPet
Example (CollectSchemas)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Components: &parser.Components{
			Schemas: map[string]*parser.Schema{
				"Pet": {
					Type: "object",
					Properties: map[string]*parser.Schema{
						"name": {Type: "string"},
						"age":  {Type: "integer"},
					},
				},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	typeCounts := make(map[string]int)
	_ = walker.Walk(result,
		walker.WithSchemaHandler(func(wc *walker.WalkContext, schema *parser.Schema) walker.Action {
			if schemaType, ok := schema.Type.(string); ok && schemaType != "" {
				typeCounts[schemaType]++
			}
			return walker.Continue
		}),
	)

	fmt.Println("object:", typeCounts["object"])
	fmt.Println("string:", typeCounts["string"])
	fmt.Println("integer:", typeCounts["integer"])
}
Output:

object: 1
string: 1
integer: 1
Example (DocumentTypeSwitch)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	oas3Doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "OAS 3.x API", Version: "1.0.0"},
	}

	result := &parser.ParseResult{
		Document:   oas3Doc,
		OASVersion: parser.OASVersion303,
	}

	_ = walker.Walk(result,
		walker.WithDocumentHandler(func(wc *walker.WalkContext, doc any) walker.Action {
			switch d := doc.(type) {
			case *parser.OAS2Document:
				fmt.Println("OAS 2.0:", d.Info.Title)
			case *parser.OAS3Document:
				fmt.Println("OAS 3.x:", d.Info.Title)
			}
			return walker.Continue
		}),
	)
}
Output:

OAS 3.x: OAS 3.x API
Example (JsonPaths)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Paths: parser.Paths{
			"/pets": &parser.PathItem{
				Get: &parser.Operation{OperationID: "getPets"},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	_ = walker.Walk(result,
		walker.WithOperationHandler(func(wc *walker.WalkContext, op *parser.Operation) walker.Action {
			fmt.Printf("Operation %s at: %s\n", op.OperationID, wc.JSONPath)
			return walker.Continue
		}),
	)
}
Output:

Operation getPets at: $.paths['/pets'].get
Example (Mutation)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Components: &parser.Components{
			Schemas: map[string]*parser.Schema{
				"Pet": {Type: "object"},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	_ = walker.Walk(result,
		walker.WithSchemaHandler(func(wc *walker.WalkContext, schema *parser.Schema) walker.Action {
			if schema.Extra == nil {
				schema.Extra = make(map[string]any)
			}
			schema.Extra["x-visited"] = true
			return walker.Continue
		}),
	)

	fmt.Printf("x-visited: %v\n", doc.Components.Schemas["Pet"].Extra["x-visited"])
}
Output:

x-visited: true
Example (SkipChildren)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Paths: parser.Paths{
			"/internal": &parser.PathItem{
				Get: &parser.Operation{OperationID: "internalOp"},
			},
			"/public": &parser.PathItem{
				Get: &parser.Operation{OperationID: "publicOp"},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	var visitedOps []string
	_ = walker.Walk(result,
		walker.WithPathHandler(func(wc *walker.WalkContext, pathItem *parser.PathItem) walker.Action {
			if wc.PathTemplate == "/internal" {
				return walker.SkipChildren
			}
			return walker.Continue
		}),
		walker.WithOperationHandler(func(wc *walker.WalkContext, op *parser.Operation) walker.Action {
			visitedOps = append(visitedOps, op.OperationID)
			return walker.Continue
		}),
	)

	fmt.Println("Visited:", visitedOps)
}
Output:

Visited: [publicOp]
Example (Stop)
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Paths: parser.Paths{
			"/a": &parser.PathItem{Get: &parser.Operation{OperationID: "opA"}},
			"/b": &parser.PathItem{Get: &parser.Operation{OperationID: "opB"}},
			"/c": &parser.PathItem{Get: &parser.Operation{OperationID: "opC"}},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	var firstOp string
	_ = walker.Walk(result,
		walker.WithOperationHandler(func(wc *walker.WalkContext, op *parser.Operation) walker.Action {
			firstOp = op.OperationID
			return walker.Stop
		}),
	)

	fmt.Println("First operation:", firstOp)
}
Output:

First operation: opA

func WalkWithOptions

func WalkWithOptions(opts ...Option) error

WalkWithOptions walks a document using functional options for input, handlers, and configuration. All options use the unified Option type - no adapter is needed.

Example:

walker.WalkWithOptions(
    walker.WithFilePath("openapi.yaml"),
    walker.WithSchemaHandler(func(wc *walker.WalkContext, s *parser.Schema) walker.Action {
        fmt.Println(wc.JSONPath)
        return walker.Continue
    }),
)
Example
package main

import (
	"fmt"

	"github.com/erraggy/oastools/parser"
	"github.com/erraggy/oastools/walker"
)

func main() {
	doc := &parser.OAS3Document{
		OpenAPI: "3.0.3",
		Info:    &parser.Info{Title: "Test", Version: "1.0.0"},
		Components: &parser.Components{
			Schemas: map[string]*parser.Schema{
				"Pet": {Type: "object"},
			},
		},
	}

	result := &parser.ParseResult{
		Document:   doc,
		OASVersion: parser.OASVersion303,
	}

	var schemaCount int
	_ = walker.WalkWithOptions(
		walker.WithParsed(result),
		walker.WithSchemaHandler(func(wc *walker.WalkContext, schema *parser.Schema) walker.Action {
			schemaCount++
			return walker.Continue
		}),
	)

	fmt.Println("Schema count:", schemaCount)
}
Output:

Schema count: 1

Types

type Action

type Action int

Action controls the walker's behavior after visiting a node.

const (
	// Continue continues walking normally, visiting children and siblings.
	Continue Action = iota

	// SkipChildren skips all children of the current node but continues with siblings.
	SkipChildren

	// Stop stops the walk immediately. No more nodes will be visited.
	Stop
)

func (Action) IsValid

func (a Action) IsValid() bool

IsValid returns true if the action is one of the defined constants.

func (Action) String

func (a Action) String() string

String returns a string representation of the action.

type CallbackHandler

type CallbackHandler func(wc *WalkContext, callback parser.Callback) Action

CallbackHandler is called for each Callback (OAS 3.x only). The callback name is available in wc.Name. The JSON path is in wc.JSONPath.

type DocumentHandler

type DocumentHandler func(wc *WalkContext, doc any) Action

DocumentHandler is called for the root document (OAS2Document or OAS3Document). The JSON path is available in wc.JSONPath.

type ExampleHandler

type ExampleHandler func(wc *WalkContext, example *parser.Example) Action

ExampleHandler is called for each Example. The example name is available in wc.Name. The JSON path is in wc.JSONPath.

type ExternalDocsHandler

type ExternalDocsHandler func(wc *WalkContext, extDocs *parser.ExternalDocs) Action

ExternalDocsHandler is called for each ExternalDocs. The JSON path is available in wc.JSONPath.

type HeaderHandler

type HeaderHandler func(wc *WalkContext, header *parser.Header) Action

HeaderHandler is called for each Header. The header name is available in wc.Name. The JSON path is in wc.JSONPath.

type InfoHandler

type InfoHandler func(wc *WalkContext, info *parser.Info) Action

InfoHandler is called for the Info object. The JSON path is available in wc.JSONPath.

type LinkHandler

type LinkHandler func(wc *WalkContext, link *parser.Link) Action

LinkHandler is called for each Link (OAS 3.x only). The link name is available in wc.Name. The JSON path is in wc.JSONPath.

type MediaTypeHandler

type MediaTypeHandler func(wc *WalkContext, mt *parser.MediaType) Action

MediaTypeHandler is called for each MediaType (OAS 3.x only). The media type name is available in wc.Name. The JSON path is in wc.JSONPath.

type OAS2DocumentHandler

type OAS2DocumentHandler func(wc *WalkContext, doc *parser.OAS2Document) Action

OAS2DocumentHandler is called for OAS 2.0 (Swagger) documents. The JSON path is available in wc.JSONPath.

type OAS3DocumentHandler

type OAS3DocumentHandler func(wc *WalkContext, doc *parser.OAS3Document) Action

OAS3DocumentHandler is called for OAS 3.x documents. The JSON path is available in wc.JSONPath.

type OperationHandler

type OperationHandler func(wc *WalkContext, op *parser.Operation) Action

OperationHandler is called for each Operation. The HTTP method is available in wc.Method. The JSON path is in wc.JSONPath.

type Option

type Option func(*Walker)

Option configures the Walker.

func WithCallbackHandler

func WithCallbackHandler(fn CallbackHandler) Option

WithCallbackHandler sets the handler for Callback objects (OAS 3.x only).

func WithContext added in v1.38.0

func WithContext(ctx context.Context) Option

WithContext sets the context for cancellation and deadline propagation. The context is available to handlers via wc.Context().

func WithDocumentHandler

func WithDocumentHandler(fn DocumentHandler) Option

WithDocumentHandler sets the handler for the root document.

func WithExampleHandler

func WithExampleHandler(fn ExampleHandler) Option

WithExampleHandler sets the handler for Example objects.

func WithExternalDocsHandler

func WithExternalDocsHandler(fn ExternalDocsHandler) Option

WithExternalDocsHandler sets the handler for ExternalDocs objects.

func WithFilePath

func WithFilePath(path string) Option

WithFilePath specifies a file path to parse and walk.

func WithHeaderHandler

func WithHeaderHandler(fn HeaderHandler) Option

WithHeaderHandler sets the handler for Header objects.

func WithInfoHandler

func WithInfoHandler(fn InfoHandler) Option

WithInfoHandler sets the handler for Info objects.

func WithLinkHandler

func WithLinkHandler(fn LinkHandler) Option

WithLinkHandler sets the handler for Link objects (OAS 3.x only).

func WithMaxDepth

func WithMaxDepth(depth int) Option

WithMaxDepth sets the maximum schema recursion depth. If depth is not positive, it is silently ignored and the default (100) is kept.

func WithMaxSchemaDepth

func WithMaxSchemaDepth(depth int) Option

WithMaxSchemaDepth sets the maximum schema recursion depth. If depth is not positive, it is silently ignored and the default (100) is kept.

func WithMediaTypeHandler

func WithMediaTypeHandler(fn MediaTypeHandler) Option

WithMediaTypeHandler sets the handler for MediaType objects (OAS 3.x only).

func WithOAS2DocumentHandler

func WithOAS2DocumentHandler(fn OAS2DocumentHandler) Option

WithOAS2DocumentHandler sets the handler for OAS 2.0 (Swagger) documents. This handler is called before the generic DocumentHandler.

func WithOAS3DocumentHandler

func WithOAS3DocumentHandler(fn OAS3DocumentHandler) Option

WithOAS3DocumentHandler sets the handler for OAS 3.x documents. This handler is called before the generic DocumentHandler.

func WithOperationHandler

func WithOperationHandler(fn OperationHandler) Option

WithOperationHandler sets the handler for Operation objects.

func WithParameterHandler

func WithParameterHandler(fn ParameterHandler) Option

WithParameterHandler sets the handler for Parameter objects.

func WithParsed

func WithParsed(result *parser.ParseResult) Option

WithParsed specifies a pre-parsed result to walk.

func WithPathHandler

func WithPathHandler(fn PathHandler) Option

WithPathHandler sets the handler for path entries.

func WithPathItemHandler

func WithPathItemHandler(fn PathItemHandler) Option

WithPathItemHandler sets the handler for PathItem objects.

func WithRequestBodyHandler

func WithRequestBodyHandler(fn RequestBodyHandler) Option

WithRequestBodyHandler sets the handler for RequestBody objects (OAS 3.x only).

func WithResponseHandler

func WithResponseHandler(fn ResponseHandler) Option

WithResponseHandler sets the handler for Response objects.

func WithSchemaHandler

func WithSchemaHandler(fn SchemaHandler) Option

WithSchemaHandler sets the handler for Schema objects.

func WithSchemaSkippedHandler

func WithSchemaSkippedHandler(fn SchemaSkippedHandler) Option

WithSchemaSkippedHandler sets the handler called when schemas are skipped. This handler is invoked when a schema is skipped due to depth limit ("depth") or cycle detection ("cycle").

func WithSecuritySchemeHandler

func WithSecuritySchemeHandler(fn SecuritySchemeHandler) Option

WithSecuritySchemeHandler sets the handler for SecurityScheme objects.

func WithServerHandler

func WithServerHandler(fn ServerHandler) Option

WithServerHandler sets the handler for Server objects (OAS 3.x only).

func WithTagHandler

func WithTagHandler(fn TagHandler) Option

WithTagHandler sets the handler for Tag objects.

func WithUserContext added in v1.38.0

func WithUserContext(ctx context.Context) Option

WithUserContext sets the context for cancellation and deadline propagation. The context is available to handlers via wc.Context().

type ParameterHandler

type ParameterHandler func(wc *WalkContext, param *parser.Parameter) Action

ParameterHandler is called for each Parameter. The JSON path is available in wc.JSONPath.

type PathHandler

type PathHandler func(wc *WalkContext, pathItem *parser.PathItem) Action

PathHandler is called for each path entry. The path template is available in wc.PathTemplate. The JSON path is in wc.JSONPath.

type PathItemHandler

type PathItemHandler func(wc *WalkContext, pathItem *parser.PathItem) Action

PathItemHandler is called for each PathItem. The path template is available in wc.PathTemplate. The JSON path is in wc.JSONPath.

type RequestBodyHandler

type RequestBodyHandler func(wc *WalkContext, reqBody *parser.RequestBody) Action

RequestBodyHandler is called for each RequestBody (OAS 3.x only). The JSON path is available in wc.JSONPath.

type ResponseHandler

type ResponseHandler func(wc *WalkContext, resp *parser.Response) Action

ResponseHandler is called for each Response. The status code is available in wc.StatusCode. The JSON path is in wc.JSONPath.

type SchemaHandler

type SchemaHandler func(wc *WalkContext, schema *parser.Schema) Action

SchemaHandler is called for each Schema, including nested schemas. The JSON path is available in wc.JSONPath. For named schemas, wc.Name contains the name.

type SchemaSkippedHandler

type SchemaSkippedHandler func(wc *WalkContext, reason string, schema *parser.Schema)

SchemaSkippedHandler is called when a schema is skipped due to depth limit or cycle detection. The reason parameter is either "depth" when the schema exceeds maxDepth, or "cycle" when the schema was already visited (circular reference detected). The schema parameter is the schema that was skipped. The JSON path is in wc.JSONPath.

type SecuritySchemeHandler

type SecuritySchemeHandler func(wc *WalkContext, scheme *parser.SecurityScheme) Action

SecuritySchemeHandler is called for each SecurityScheme. The scheme name is available in wc.Name. The JSON path is in wc.JSONPath.

type ServerHandler

type ServerHandler func(wc *WalkContext, server *parser.Server) Action

ServerHandler is called for each Server (OAS 3.x only). The JSON path is available in wc.JSONPath.

type TagHandler

type TagHandler func(wc *WalkContext, tag *parser.Tag) Action

TagHandler is called for each Tag. The JSON path is available in wc.JSONPath.

type WalkContext added in v1.38.0

type WalkContext struct {
	// JSONPath is the full JSON path to the current node.
	// Always populated. Example: "$.paths['/pets'].get.responses['200']"
	JSONPath string

	// PathTemplate is the URL path template when walking within $.paths scope.
	// Empty when not in paths scope. Example: "/pets/{petId}"
	PathTemplate string

	// Method is the HTTP method when walking within an operation scope.
	// Empty when not in operation scope. Example: "get", "post"
	Method string

	// StatusCode is the HTTP status code when walking within a response scope.
	// Empty when not in response scope. Example: "200", "default"
	StatusCode string

	// Name is the map key for named items like headers, schemas, examples, etc.
	// Empty for array items or root-level objects. Example: "Pet", "X-Rate-Limit"
	Name string

	// IsComponent is true when the current node is within the components section
	// (OAS 3.x) or definitions/parameters/responses at document root (OAS 2.x).
	IsComponent bool
	// contains filtered or unexported fields
}

WalkContext provides contextual information about the current node being visited. It follows the http.Request pattern for context access.

func (*WalkContext) Context added in v1.38.0

func (wc *WalkContext) Context() context.Context

Context returns the context.Context for cancellation and deadline propagation. Returns context.Background() if no context was set.

func (*WalkContext) InOperationScope added in v1.38.0

func (wc *WalkContext) InOperationScope() bool

InOperationScope returns true if currently walking within an operation.

func (*WalkContext) InPathsScope added in v1.38.0

func (wc *WalkContext) InPathsScope() bool

InPathsScope returns true if currently walking within $.paths.

func (*WalkContext) InResponseScope added in v1.38.0

func (wc *WalkContext) InResponseScope() bool

InResponseScope returns true if currently walking within a response.

func (*WalkContext) WithContext added in v1.38.0

func (wc *WalkContext) WithContext(ctx context.Context) *WalkContext

WithContext returns a shallow copy of WalkContext with the new context.

type Walker

type Walker struct {
	// contains filtered or unexported fields
}

Walker traverses OpenAPI documents and calls handlers for each node type.

func New

func New() *Walker

New creates a new Walker with default settings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL