Documentation
¶
Overview ¶
Package joiner provides joining for multiple OpenAPI Specification documents.
The joiner merges multiple OAS documents of the same major version into a single document. It supports OAS 2.0 documents with other 2.0 documents, and all OAS 3.x versions together (3.0.x, 3.1.x, 3.2.x). It uses the version and format (JSON or YAML) from the first document as the result version and format, ensuring format consistency when writing output with WriteResult.
Configuration ¶
Always use DefaultConfig to create a JoinerConfig instance. Direct struct instantiation (e.g., JoinerConfig{}) is not recommended as it leaves required fields at their zero values, which may cause unexpected behavior:
- NamespacePrefix will be nil (causes nil map panics if accessed)
- RenameTemplate will be empty (falls back to default, but unclear intent)
- All strategies default to empty string (treated as StrategyFailOnCollision)
Correct usage:
config := joiner.DefaultConfig() // Always start with defaults config.PathStrategy = joiner.StrategyAcceptLeft // Then customize as needed
Incorrect usage:
config := joiner.JoinerConfig{} // Zero values - avoid this!
config.PathStrategy = joiner.StrategyAcceptLeft
Quick Start ¶
Join files using functional options:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"base.yaml", "ext.yaml"}),
joiner.WithPathStrategy(joiner.StrategyAcceptLeft),
)
if err != nil {
log.Fatal(err)
}
_ = joiner.WriteResult(result, "merged.yaml")
Or use a full config with options:
config := joiner.DefaultConfig()
config.PathStrategy = joiner.StrategyAcceptLeft
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"base.yaml", "ext.yaml"}),
joiner.WithConfig(config),
)
Or create a reusable Joiner instance:
j := joiner.New(joiner.DefaultConfig())
result1, _ := j.Join([]string{"api1-base.yaml", "api1-ext.yaml"})
result2, _ := j.Join([]string{"api2-base.yaml", "api2-ext.yaml"})
j.WriteResult(result1, "merged1.yaml")
j.WriteResult(result2, "merged2.yaml")
Collision Strategies ¶
Control how collisions between documents are handled:
- StrategyFailOnCollision: Fail on any collision (default)
- StrategyAcceptLeft: Keep value from first document
- StrategyAcceptRight: Keep value from last document
- StrategyFailOnPaths: Fail only on path collisions, allow schema merging
- StrategyRenameLeft: Rename left schema, keep right under original name
- StrategyRenameRight: Rename right schema, keep left under original name
- StrategyDeduplicateEquivalent: Merge structurally identical schemas
Set strategies globally (DefaultStrategy) or per component type (PathStrategy, SchemaStrategy, ComponentStrategy). The rename and deduplicate strategies provide advanced collision handling with automatic reference rewriting.
Advanced Collision Handling ¶
The rename strategies preserve both colliding schemas by renaming one and automatically updating all references throughout the merged document:
config := joiner.DefaultConfig()
config.SchemaStrategy = joiner.StrategyRenameRight
config.RenameTemplate = "{{.Name}}_{{.Source}}"
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"users-api.yaml", "billing-api.yaml"}),
joiner.WithConfig(config),
)
The deduplicate strategy uses semantic equivalence detection to merge structurally identical schemas while failing on true structural conflicts:
config := joiner.DefaultConfig()
config.SchemaStrategy = joiner.StrategyDeduplicateEquivalent
config.EquivalenceMode = "deep"
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"base.yaml", "ext.yaml"}),
joiner.WithConfig(config),
)
See the examples in example_test.go for more configuration patterns.
Operation-Aware Renaming ¶
When renaming schemas to resolve collisions, you can enable operation context to trace schemas back to their originating operations. This provides rich context (Path, Method, OperationID, Tags, UsageType) for generating meaningful names.
Enable via option:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"users-api.yaml", "billing-api.yaml"}),
joiner.WithSchemaStrategy(joiner.StrategyRenameRight),
joiner.WithRenameTemplate("{{.Name}}_{{.OperationID}}"),
joiner.WithOperationContext(true),
)
Or via config:
config := joiner.DefaultConfig()
config.SchemaStrategy = joiner.StrategyRenameRight
config.OperationContext = true
config.RenameTemplate = "{{.Name}}_{{pathResource .Path | pascalCase}}"
RenameContext Fields ¶
The RenameContext struct provides comprehensive context for rename templates.
Core fields (always available):
Name string // Original schema name Source string // Source file name (sanitized, no extension) Index int // Document index (0-based)
Operation context (requires WithOperationContext(true)):
Path string // API path: "/users/{id}"
Method string // HTTP method: "get", "post"
OperationID string // Operation ID if defined
Tags []string // Tags from primary operation
UsageType string // "request", "response", "parameter", "header", "callback"
StatusCode string // Response status code (for response usage)
ParamName string // Parameter name (for parameter usage)
MediaType string // Media type: "application/json"
Aggregate context (for schemas referenced by multiple operations):
AllPaths []string // All referencing paths AllMethods []string // All methods (deduplicated) AllOperationIDs []string // All operation IDs (non-empty only) AllTags []string // All tags (deduplicated) RefCount int // Total operation references PrimaryResource string // Extracted resource name from path IsShared bool // True if referenced by multiple operations
Template Functions ¶
The following template functions are available in rename templates.
Path functions:
pathSegment(path, index) // Extract nth segment (negative = from end)
pathResource(path) // First non-parameter segment
pathLast(path) // Last non-parameter segment
pathClean(path) // Sanitize for naming: "/users/{id}" -> "users_id"
Tag functions:
firstTag(tags) // First tag or empty string joinTags(tags, sep) // Join tags with separator hasTag(tags, tag) // Check if tag is present
Case transformation functions:
pascalCase(s) // "user_name" -> "UserName" camelCase(s) // "user_name" -> "userName" snakeCase(s) // "UserName" -> "user_name" kebabCase(s) // "UserName" -> "user-name"
Conditional helpers:
default(value, fallback) // Return fallback if value is empty coalesce(values...) // Return first non-empty value
Example templates:
// Use operation ID or fall back to source file
"{{.Name}}_{{coalesce .OperationID .Source}}"
// Use primary resource in PascalCase
"{{.Name}}_{{pathResource .Path | pascalCase}}"
// Include first tag if available
"{{.Name}}_{{default (firstTag .Tags) .Source}}"
// Clean path for naming
"{{.Name}}_{{pathClean .Path}}"
Primary Operation Policy ¶
When a schema is referenced by multiple operations, the policy determines which operation provides the primary context values (Path, Method, etc.).
config := joiner.DefaultConfig() config.PrimaryOperationPolicy = joiner.PolicyMostSpecific
Or via option:
joiner.WithPrimaryOperationPolicy(joiner.PolicyMostSpecific)
Available policies:
- PolicyFirstEncountered: Use the first operation found during traversal (default)
- PolicyMostSpecific: Prefer operations with operationId, then those with tags
- PolicyAlphabetical: Sort by path+method and use alphabetically first
Overlay Integration ¶
Apply overlays during the join process for pre-processing inputs or post-processing results:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"base.yaml", "ext.yaml"}),
joiner.WithPreJoinOverlayFile("normalize.yaml"), // Applied to each input
joiner.WithPostJoinOverlayFile("enhance.yaml"), // Applied to merged result
)
Pre-join overlays are applied to each input document before merging. Post-join overlays are applied to the final merged result.
Semantic Schema Deduplication ¶
After merging, the joiner can automatically identify and consolidate structurally identical schemas across all input documents. This reduces document size when multiple APIs happen to define equivalent types with different names.
Enable via option:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"users-api.yaml", "orders-api.yaml"}),
joiner.WithSemanticDeduplication(true),
)
Or via config:
config := joiner.DefaultConfig()
config.SemanticDeduplication = true
j := joiner.New(config)
result, _ := j.Join([]string{"api1.yaml", "api2.yaml"})
When schemas from different documents are structurally equivalent (same type, properties, constraints), they are consolidated into a single canonical schema (alphabetically first name). All $ref references throughout the merged document are automatically rewritten.
This differs from the StrategyDeduplicateEquivalent collision strategy which only handles same-named collisions. Semantic deduplication works across all schemas regardless of their original names.
Features and Limitations ¶
The joiner validates all input documents, prevents output file overwrites with restrictive 0600 permissions, deduplicates tags, and optionally merges arrays (servers, security, tags). It uses the info object from the first document; subsequent info sections are ignored.
External References ¶
The joiner preserves external $ref values but does NOT resolve or merge them. This is intentional to avoid ambiguity and maintain document structure.
If your documents contain external references, you have two options:
Resolve references before joining: Use parser.ParseWithOptions(parser.WithResolveRefs(true)) before joining
Keep external references and resolve after joining: Join the documents, then parse the result with WithResolveRefs(true)
Example with external references:
// Document 1: base.yaml // paths: // /users: // get: // responses: // 200: // schema: // $ref: "./schemas/user.yaml#/User" // // Document 2: extension.yaml // paths: // /posts: // get: // responses: // 200: // schema: // $ref: "./schemas/post.yaml#/Post" // // After joining, both $ref values are preserved in the merged document. // Use parser.WithResolveRefs(true) to resolve them if needed.
Structured Warnings ¶
The joiner provides structured warnings through the JoinWarning type, which includes detailed context about non-fatal issues encountered during document joining. Each warning has a category, source location, and optional context data for programmatic handling.
Access structured warnings from the result:
result, _ := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"base.yaml", "ext.yaml"}),
)
for _, w := range result.StructuredWarnings {
fmt.Printf("[%s] %s\n", w.Category, w.Message)
if w.HasLocation() {
fmt.Printf(" at %s\n", w.Location())
}
}
Filter warnings by category or severity:
pathCollisions := result.StructuredWarnings.ByCategory(joiner.WarnPathCollision) criticalWarnings := result.StructuredWarnings.BySeverity(severity.SeverityWarning)
Warning categories include:
- WarnVersionMismatch: Documents have different minor versions
- WarnPathCollision: Path collision was resolved by strategy
- WarnSchemaCollision: Schema/definition collision was resolved
- WarnWebhookCollision: Webhook collision was resolved
- WarnSchemaRenamed: Schema was renamed due to collision
- WarnSchemaDeduplicated: Schema was deduplicated (structurally equivalent)
- WarnNamespacePrefixed: Namespace prefix was applied
- WarnMetadataOverride: Metadata was overridden (host, basePath)
- WarnGenericSourceName: Document has a generic source name (e.g., "ParseBytes.yaml")
For backward compatibility, warnings are also available as []string via result.Warnings.
Pre-Parsed Documents and Source Names ¶
When using Joiner.JoinParsed with pre-parsed documents (the recommended path for high performance), ensure each document has a meaningful SourcePath set. The joiner uses SourcePath in collision reports and warnings. Without meaningful names, collision reports show unhelpful text like "ParseBytes.yaml vs ParseBytes.yaml".
Set meaningful source names before joining:
// Fetch and parse specs from your services
specs := make([]parser.ParseResult, 0, len(services))
for name, data := range serviceSpecs {
result, _ := parser.ParseWithOptions(parser.WithBytes(data))
result.SourcePath = name // Set meaningful name for collision reports
specs = append(specs, *result)
}
// Collision reports now show "users-api vs billing-api"
joined, _ := joiner.JoinParsed(specs)
Alternatively, use parser.WithSourceName when parsing:
result, _ := parser.ParseWithOptions(
parser.WithBytes(data),
parser.WithSourceName("users-api"),
)
The joiner emits an info-level WarnGenericSourceName warning when documents have generic source names (empty, "ParseBytes.*", "ParseReader.*") to help identify this issue. These warnings are:
- Emitted for each affected document at the start of joining
- Info-level (non-blocking) - joining proceeds normally
- Available via result.StructuredWarnings.ByCategory(WarnGenericSourceName)
- Also included in result.Warnings string slice for backward compatibility
Chaining with Other Packages ¶
Use JoinResult.ToParseResult to convert the join result for use with other packages:
// Join documents
joinResult, _ := joiner.JoinWithOptions(
joiner.WithFilePaths([]string{"users-api.yaml", "orders-api.yaml"}),
)
// Validate the joined result
v := validator.New()
validationResult, _ := v.ValidateParsed(*joinResult.ToParseResult())
// Or convert to a different version
c := converter.New()
convResult, _ := c.ConvertParsed(*joinResult.ToParseResult(), "3.1.0")
// Or diff against another document
diffResult, _ := differ.DiffWithOptions(
differ.WithSourceParsed(*joinResult.ToParseResult()),
differ.WithTargetFilePath("production.yaml"),
)
The returned parser.ParseResult has Document populated (the typed OAS document) and all metadata fields (Version, OASVersion, SourceFormat, Stats, Warnings). The Data field is nil as downstream consumers use Document, not Data.
Related Packages ¶
The joiner integrates with other oastools packages:
- github.com/erraggy/oastools/parser - Parse specifications before joining
- github.com/erraggy/oastools/validator - Validate documents before joining (required)
- github.com/erraggy/oastools/fixer - Fix common validation errors before joining
- github.com/erraggy/oastools/converter - Convert between OAS versions before joining
- github.com/erraggy/oastools/differ - Compare joined results with original documents
- github.com/erraggy/oastools/generator - Generate code from joined specifications
- github.com/erraggy/oastools/builder - Programmatically build specifications to join
- github.com/erraggy/oastools/overlay - Apply overlay transformations during join
Example ¶
Example demonstrates basic usage of the joiner to combine two OpenAPI specifications.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/erraggy/oastools/joiner"
)
func main() {
outputPath := filepath.Join(os.TempDir(), "joined-example.yaml")
defer func() { _ = os.Remove(outputPath) }()
config := joiner.DefaultConfig()
j := joiner.New(config)
result, err := j.Join([]string{
"../testdata/join-base-3.0.yaml",
"../testdata/join-extension-3.0.yaml",
})
if err != nil {
log.Fatalf("failed to join: %v", err)
}
err = j.WriteResult(result, outputPath)
if err != nil {
log.Fatalf("failed to write result: %v", err)
}
fmt.Printf("Version: %s\n", result.Version)
fmt.Printf("Warnings: %d\n", len(result.Warnings))
}
Output: Version: 3.0.3 Warnings: 0
Example (CustomStrategies) ¶
Example_customStrategies demonstrates using custom collision strategies for different component types.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/erraggy/oastools/joiner"
)
func main() {
outputPath := filepath.Join(os.TempDir(), "joined-custom.yaml")
defer func() { _ = os.Remove(outputPath) }()
config := joiner.JoinerConfig{
DefaultStrategy: joiner.StrategyFailOnCollision,
PathStrategy: joiner.StrategyFailOnPaths,
SchemaStrategy: joiner.StrategyAcceptLeft,
ComponentStrategy: joiner.StrategyAcceptRight,
DeduplicateTags: true,
MergeArrays: true,
}
j := joiner.New(config)
result, err := j.Join([]string{
"../testdata/join-base-3.0.yaml",
"../testdata/join-extension-3.0.yaml",
})
if err != nil {
log.Fatalf("failed to join: %v", err)
}
err = j.WriteResult(result, outputPath)
if err != nil {
log.Fatalf("failed to write result: %v", err)
}
fmt.Printf("Joined successfully\n")
fmt.Printf("Collisions resolved: %d\n", result.CollisionCount)
}
Output: Joined successfully Collisions resolved: 0
Example (OperationContext) ¶
Example_operationContext demonstrates operation-aware schema renaming. When OperationContext is enabled, the joiner traces schemas back to their originating operations, allowing rename templates to use fields like OperationID, Path, Method, and Tags.
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/joiner"
)
func main() {
// Join two specs that both define a "Response" schema.
// Without operation context, collisions are resolved using Source/Index.
// With operation context, we can use operationId for meaningful names.
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths(
"../testdata/join-operation-context-users-3.0.yaml",
"../testdata/join-operation-context-orders-3.0.yaml",
),
joiner.WithOperationContext(true),
joiner.WithSchemaStrategy(joiner.StrategyRenameRight),
// Template uses OperationID with PascalCase conversion
joiner.WithRenameTemplate("{{.OperationID | pascalCase}}{{.Name}}"),
)
if err != nil {
log.Fatalf("failed to join: %v", err)
}
// The colliding "Response" schema from the orders API is renamed using
// the operationId "createOrder" -> "CreateOrderResponse"
fmt.Printf("Collisions resolved: %d\n", result.CollisionCount)
fmt.Printf("Version: %s\n", result.Version)
}
Output: Collisions resolved: 1 Version: 3.0.0
Example (PreParsedSourceNames) ¶
Example_preParsedSourceNames demonstrates setting meaningful source names when joining pre-parsed documents. This is the recommended workflow for high-performance joining (150x faster than file-based joining) while maintaining clear collision reports.
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/joiner"
"github.com/erraggy/oastools/parser"
)
func main() {
// Simulate fetching API specs from multiple microservices
// In practice, these would come from HTTP endpoints or a database
usersSpec := []byte(`openapi: "3.0.0"
info:
title: Users API
version: "1.0"
paths:
/users:
get:
summary: List users
responses:
'200':
description: OK
`)
ordersSpec := []byte(`openapi: "3.0.0"
info:
title: Orders API
version: "1.0"
paths:
/orders:
get:
summary: List orders
responses:
'200':
description: OK
`)
// Parse each spec with a meaningful source name
usersResult, err := parser.ParseWithOptions(
parser.WithBytes(usersSpec),
parser.WithSourceName("users-api"), // Set meaningful name
)
if err != nil {
log.Fatal(err)
}
ordersResult, err := parser.ParseWithOptions(
parser.WithBytes(ordersSpec),
parser.WithSourceName("orders-api"), // Set meaningful name
)
if err != nil {
log.Fatal(err)
}
// Join pre-parsed documents (150x faster than file-based)
j := joiner.New(joiner.DefaultConfig())
result, err := j.JoinParsed([]parser.ParseResult{*usersResult, *ordersResult})
if err != nil {
log.Fatal(err)
}
// Collision reports now show "users-api vs orders-api" instead of
// "ParseBytes.yaml vs ParseBytes.yaml"
fmt.Printf("Joined successfully\n")
fmt.Printf("Version: %s\n", result.Version)
fmt.Printf("Warnings: %d\n", len(result.Warnings))
}
Output: Joined successfully Version: 3.0.0 Warnings: 0
Example (PrimaryOperationPolicy) ¶
Example_primaryOperationPolicy demonstrates how to select which operation provides the primary context when a schema is referenced by multiple operations. PolicyMostSpecific prefers operations with operationId over those without.
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/joiner"
)
func main() {
// When a schema is referenced by multiple operations, the policy determines
// which operation's context is used for the primary fields (Path, Method,
// OperationID). PolicyMostSpecific chooses the operation with the richest
// metadata: first preferring those with operationId, then those with tags.
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths(
"../testdata/join-operation-context-users-3.0.yaml",
"../testdata/join-operation-context-orders-3.0.yaml",
),
joiner.WithOperationContext(true),
joiner.WithPrimaryOperationPolicy(joiner.PolicyMostSpecific),
joiner.WithSchemaStrategy(joiner.StrategyRenameRight),
// Use operationId if available; fallback to path resource
joiner.WithRenameTemplate("{{coalesce .OperationID (pathResource .Path) .Source | pascalCase}}{{.Name}}"),
)
if err != nil {
log.Fatalf("failed to join: %v", err)
}
// With PolicyMostSpecific, operations with operationId are preferred
fmt.Printf("Collisions resolved: %d\n", result.CollisionCount)
fmt.Printf("Version: %s\n", result.Version)
}
Output: Collisions resolved: 1 Version: 3.0.0
Example (SemanticDeduplication) ¶
Example_semanticDeduplication demonstrates automatic consolidation of identical schemas across multiple OpenAPI documents. When documents share structurally identical schemas (even if named differently), semantic deduplication identifies these duplicates and consolidates them to a single canonical schema.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/erraggy/oastools/joiner"
)
func main() {
outputPath := filepath.Join(os.TempDir(), "joined-dedup.yaml")
defer func() { _ = os.Remove(outputPath) }()
// Enable semantic deduplication in the joiner configuration
config := joiner.JoinerConfig{
DefaultStrategy: joiner.StrategyAcceptLeft,
SemanticDeduplication: true, // Enable schema deduplication
EquivalenceMode: "deep", // Use deep structural comparison
DeduplicateTags: true,
MergeArrays: true,
}
j := joiner.New(config)
result, err := j.Join([]string{
"../testdata/join-base-3.0.yaml",
"../testdata/join-extension-3.0.yaml",
})
if err != nil {
log.Fatalf("failed to join: %v", err)
}
err = j.WriteResult(result, outputPath)
if err != nil {
log.Fatalf("failed to write result: %v", err)
}
// Semantic deduplication identifies structurally equivalent schemas
// across documents and consolidates them, reducing duplication in the
// merged output. The alphabetically-first name becomes canonical.
fmt.Printf("Joined successfully\n")
fmt.Printf("Version: %s\n", result.Version)
}
Output: Joined successfully Version: 3.0.3
Example (TemplateFunctions) ¶
Example_templateFunctions demonstrates the template functions available for operation-aware renaming: pathResource, pathClean, case conversions, and fallback functions like default and coalesce.
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/joiner"
)
func main() {
// Template using pathResource to extract "orders" from "/orders"
// and pascalCase to convert it to "Orders"
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths(
"../testdata/join-operation-context-users-3.0.yaml",
"../testdata/join-operation-context-orders-3.0.yaml",
),
joiner.WithOperationContext(true),
joiner.WithSchemaStrategy(joiner.StrategyRenameRight),
// pathResource extracts the first path segment (e.g., "/orders" -> "orders")
// pascalCase converts to PascalCase (e.g., "orders" -> "Orders")
joiner.WithRenameTemplate("{{pathResource .Path | pascalCase}}{{.Name}}"),
)
if err != nil {
log.Fatalf("failed to join: %v", err)
}
// The colliding "Response" schema from /orders becomes "OrdersResponse"
fmt.Printf("Collisions resolved: %d\n", result.CollisionCount)
fmt.Printf("Version: %s\n", result.Version)
}
Output: Collisions resolved: 1 Version: 3.0.0
Example (ToParseResult) ¶
Example_toParseResult demonstrates using ToParseResult() to chain joiner output with other packages like validator, fixer, converter, or differ.
package main
import (
"fmt"
"log"
"github.com/erraggy/oastools/joiner"
)
func main() {
// Join two OpenAPI specifications
joinResult, err := joiner.JoinWithOptions(
joiner.WithFilePaths(
"../testdata/join-base-3.0.yaml",
"../testdata/join-extension-3.0.yaml",
),
)
if err != nil {
log.Fatal(err)
}
// Convert to ParseResult for use with validator, fixer, differ, etc.
parseResult := joinResult.ToParseResult()
// The ParseResult can now be used with other packages:
// - validator.ValidateParsed(*parseResult)
// - fixer.FixParsed(*parseResult)
// - differ.DiffParsed(*baseResult, *parseResult)
// - converter.ConvertParsed(*parseResult, "3.1.0")
fmt.Printf("Source: %s\n", parseResult.SourcePath)
fmt.Printf("Version: %s\n", parseResult.Version)
fmt.Printf("Has document: %v\n", parseResult.Document != nil)
}
Output: Source: ../testdata/join-base-3.0.yaml Version: 3.0.3 Has document: true
Index ¶
- func IsGenericSourceName(sourcePath string) bool
- func IsValidEquivalenceMode(mode string) bool
- func IsValidStrategy(strategy string) bool
- func ValidEquivalenceModes() []string
- func ValidStrategies() []string
- type CollisionError
- type CollisionEvent
- type CollisionReport
- type CollisionStrategy
- type EquivalenceMode
- type EquivalenceResult
- type JoinResult
- type JoinWarning
- func NewGenericSourceNameWarning(sourcePath string, docIndex int) *JoinWarning
- func NewMetadataOverrideWarning(field, firstValue, secondValue, secondFile string) *JoinWarning
- func NewNamespacePrefixWarning(originalName, newName, section, sourceFile string, line, col int) *JoinWarning
- func NewPathCollisionWarning(path, resolution, firstFile, secondFile string, line, col int) *JoinWarning
- func NewSchemaCollisionWarning(schemaName, resolution, section, firstFile, secondFile string, line, col int) *JoinWarning
- func NewSchemaDedupWarning(schemaName, section, sourceFile string, line, col int) *JoinWarning
- func NewSchemaRenamedWarning(originalName, newName, section, sourceFile string, line, col int, ...) *JoinWarning
- func NewSemanticDedupSummaryWarning(count int, section string) *JoinWarning
- func NewVersionMismatchWarning(file1, version1, file2, version2, targetVersion string) *JoinWarning
- func NewWebhookCollisionWarning(webhookName, resolution, firstFile, secondFile string, line, col int) *JoinWarning
- type JoinWarnings
- type Joiner
- type JoinerConfig
- type OperationRef
- type Option
- func WithAlwaysApplyPrefix(enabled bool) Option
- func WithCollisionReport(enabled bool) Option
- func WithComponentStrategy(strategy CollisionStrategy) Option
- func WithConfig(config JoinerConfig) Option
- func WithDeduplicateTags(enabled bool) Option
- func WithDefaultStrategy(strategy CollisionStrategy) Option
- func WithEquivalenceMode(mode string) Option
- func WithFilePaths(paths ...string) Option
- func WithMergeArrays(enabled bool) Option
- func WithNamespacePrefix(sourcePath, prefix string) Option
- func WithOperationContext(enabled bool) Option
- func WithParsed(docs ...parser.ParseResult) Option
- func WithPathStrategy(strategy CollisionStrategy) Option
- func WithPostJoinOverlay(o *overlay.Overlay) Option
- func WithPostJoinOverlayFile(path string) Option
- func WithPreJoinOverlay(o *overlay.Overlay) Option
- func WithPreJoinOverlayFile(path string) Option
- func WithPrimaryOperationPolicy(policy PrimaryOperationPolicy) Option
- func WithRenameTemplate(template string) Option
- func WithSchemaStrategy(strategy CollisionStrategy) Option
- func WithSemanticDeduplication(enabled bool) Option
- func WithSourceMaps(maps map[string]*parser.SourceMap) Option
- func WithSpecOverlay(specIdentifier string, o *overlay.Overlay) Option
- func WithSpecOverlayFile(specIdentifier, overlayPath string) Option
- type PrimaryOperationPolicy
- type RefGraph
- type RenameContext
- type SchemaDifference
- type SchemaRef
- type SchemaRewriter
- type UsageType
- type WarningCategory
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsGenericSourceName ¶ added in v1.40.0
IsGenericSourceName returns true if the source path appears to be a generic parser-generated name rather than a meaningful identifier. Generic names include empty strings and default names like "ParseBytes.yaml".
func IsValidEquivalenceMode ¶ added in v1.23.0
IsValidEquivalenceMode checks if an equivalence mode string is valid
func IsValidStrategy ¶
IsValidStrategy checks if a strategy string is valid
func ValidEquivalenceModes ¶ added in v1.23.0
func ValidEquivalenceModes() []string
ValidEquivalenceModes returns all valid equivalence mode strings
func ValidStrategies ¶
func ValidStrategies() []string
ValidStrategies returns all valid collision strategy strings
Types ¶
type CollisionError ¶
type CollisionError struct {
Section string
Key string
FirstFile string
FirstPath string
FirstLine int // 1-based line number in first file (0 if unknown)
FirstColumn int // 1-based column number in first file (0 if unknown)
SecondFile string
SecondPath string
SecondLine int // 1-based line number in second file (0 if unknown)
SecondColumn int // 1-based column number in second file (0 if unknown)
Strategy CollisionStrategy
}
CollisionError provides detailed information about a collision
func (*CollisionError) Error ¶
func (e *CollisionError) Error() string
type CollisionEvent ¶ added in v1.23.0
type CollisionEvent struct {
SchemaName string
LeftSource string
LeftLine int // 1-based line number in left source (0 if unknown)
LeftColumn int // 1-based column number in left source (0 if unknown)
RightSource string
RightLine int // 1-based line number in right source (0 if unknown)
RightColumn int // 1-based column number in right source (0 if unknown)
Strategy CollisionStrategy
Resolution string // "renamed", "deduplicated", "kept-left", "kept-right", "failed"
NewName string // For rename resolutions
Differences []SchemaDifference
Severity severity.Severity
}
CollisionEvent represents a single collision occurrence with resolution details
type CollisionReport ¶ added in v1.23.0
type CollisionReport struct {
TotalCollisions int
ResolvedByRename int
ResolvedByDedup int
ResolvedByAccept int
FailedCollisions int
Events []CollisionEvent
}
CollisionReport provides detailed analysis of collisions encountered during join operations
func NewCollisionReport ¶ added in v1.23.0
func NewCollisionReport() *CollisionReport
NewCollisionReport creates an empty collision report
func (*CollisionReport) AddEvent ¶ added in v1.23.0
func (r *CollisionReport) AddEvent(event CollisionEvent)
AddEvent adds a collision event to the report and updates counters
func (*CollisionReport) GetByResolution ¶ added in v1.23.0
func (r *CollisionReport) GetByResolution(resolution string) []CollisionEvent
GetByResolution returns events with a specific resolution type
func (*CollisionReport) GetCriticalEvents ¶ added in v1.23.0
func (r *CollisionReport) GetCriticalEvents() []CollisionEvent
GetCriticalEvents returns events with Critical severity
func (*CollisionReport) HasFailures ¶ added in v1.23.0
func (r *CollisionReport) HasFailures() bool
HasFailures returns true if any collisions failed to resolve
type CollisionStrategy ¶
type CollisionStrategy string
CollisionStrategy defines how to handle collisions when merging documents
const ( // StrategyAcceptLeft keeps values from the first document when collisions occur StrategyAcceptLeft CollisionStrategy = "accept-left" // StrategyAcceptRight keeps values from the last document when collisions occur (overwrites) StrategyAcceptRight CollisionStrategy = "accept-right" // StrategyFailOnCollision returns an error if any collision is detected StrategyFailOnCollision CollisionStrategy = "fail" // StrategyFailOnPaths fails only on path collisions, allows schema/component collisions StrategyFailOnPaths CollisionStrategy = "fail-on-paths" // StrategyRenameLeft keeps the right-side schema and renames the left-side schema StrategyRenameLeft CollisionStrategy = "rename-left" // StrategyRenameRight keeps the left-side schema and renames the right-side schema StrategyRenameRight CollisionStrategy = "rename-right" // StrategyDeduplicateEquivalent uses semantic comparison to deduplicate structurally identical schemas StrategyDeduplicateEquivalent CollisionStrategy = "deduplicate" )
type EquivalenceMode ¶ added in v1.23.0
type EquivalenceMode string
EquivalenceMode defines how deeply to compare schemas
const ( // EquivalenceModeNone disables equivalence detection EquivalenceModeNone EquivalenceMode = "none" // EquivalenceModeShallow compares only top-level schema properties EquivalenceModeShallow EquivalenceMode = "shallow" // EquivalenceModeDeep recursively compares all nested schemas EquivalenceModeDeep EquivalenceMode = "deep" )
type EquivalenceResult ¶ added in v1.23.0
type EquivalenceResult struct {
Equivalent bool
Differences []SchemaDifference
}
EquivalenceResult contains the outcome of schema comparison
func CompareSchemas ¶ added in v1.23.0
func CompareSchemas(left, right *parser.Schema, mode EquivalenceMode) EquivalenceResult
CompareSchemas compares two schemas for structural equivalence Ignores: description, title, example, deprecated, and extension fields (x-*)
type JoinResult ¶
type JoinResult struct {
// Document contains the joined document (*parser.OAS2Document or *parser.OAS3Document)
Document any
// Version is the OpenAPI version of the joined document
Version string
// OASVersion is the enumerated version
OASVersion parser.OASVersion
// SourceFormat is the format of the first source file (JSON or YAML)
SourceFormat parser.SourceFormat
// Warnings contains non-fatal issues encountered during joining (for backward compatibility)
Warnings []string
// StructuredWarnings contains detailed warning information with context
StructuredWarnings JoinWarnings
// CollisionCount tracks the number of collisions resolved
CollisionCount int
// Stats contains statistical information about the joined document
Stats parser.DocumentStats
// CollisionDetails contains detailed collision analysis (when CollisionReport is enabled)
CollisionDetails *CollisionReport
// contains filtered or unexported fields
}
JoinResult contains the joined OpenAPI specification and metadata
func JoinWithOptions ¶ added in v1.11.0
func JoinWithOptions(opts ...Option) (*JoinResult, error)
JoinWithOptions joins multiple OpenAPI specifications using functional options. This provides a flexible, extensible API that combines input source selection and configuration in a single function call.
When overlay options are provided, the join process follows these steps:
- Parse all input specifications
- Apply pre-join overlays to all specs (in order specified)
- Apply per-spec overlays to their respective specs
- Perform the join operation
- Apply post-join overlay to the merged result
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("api1.yaml", "api2.yaml"),
joiner.WithPathStrategy(joiner.StrategyAcceptLeft),
joiner.WithPreJoinOverlayFile("normalize.yaml"),
joiner.WithPostJoinOverlayFile("enhance.yaml"),
)
func (*JoinResult) AddWarning ¶ added in v1.35.0
func (r *JoinResult) AddWarning(w *JoinWarning)
AddWarning adds a structured warning and populates the legacy Warnings slice.
func (*JoinResult) ToParseResult ¶ added in v1.40.0
func (r *JoinResult) ToParseResult() *parser.ParseResult
ToParseResult converts the JoinResult to a ParseResult for use with other packages like validator, fixer, converter, and differ. The returned ParseResult has Document populated but Data is nil (consumers use Document, not Data).
func (*JoinResult) WarningStrings ¶ added in v1.35.0
func (r *JoinResult) WarningStrings() []string
WarningStrings returns warning messages for backward compatibility. Deprecated: Use StructuredWarnings directly for detailed information.
type JoinWarning ¶ added in v1.35.0
type JoinWarning struct {
// Category identifies the type of warning.
Category WarningCategory
// Path is the JSON path to the affected element.
Path string
// Message is a human-readable description.
Message string
// SourceFile is the file that triggered the warning.
SourceFile string
// Line is the 1-based line number (0 if unknown).
Line int
// Column is the 1-based column number (0 if unknown).
Column int
// Severity indicates warning severity (default: SeverityWarning).
Severity severity.Severity
// Context provides additional details.
Context map[string]any
}
JoinWarning represents a structured warning from the joiner package. It provides detailed context about non-fatal issues encountered during document joining.
func NewGenericSourceNameWarning ¶ added in v1.40.0
func NewGenericSourceNameWarning(sourcePath string, docIndex int) *JoinWarning
NewGenericSourceNameWarning creates a warning when a document has a generic source name. This helps users identify that collision reports may be unclear and guides them to set meaningful source names using ParseResult.SourcePath.
func NewMetadataOverrideWarning ¶ added in v1.35.0
func NewMetadataOverrideWarning(field, firstValue, secondValue, secondFile string) *JoinWarning
NewMetadataOverrideWarning creates a warning when metadata is overridden.
func NewNamespacePrefixWarning ¶ added in v1.35.0
func NewNamespacePrefixWarning(originalName, newName, section, sourceFile string, line, col int) *JoinWarning
NewNamespacePrefixWarning creates a warning when a namespace prefix is applied.
func NewPathCollisionWarning ¶ added in v1.35.0
func NewPathCollisionWarning(path, resolution, firstFile, secondFile string, line, col int) *JoinWarning
NewPathCollisionWarning creates a warning for path collisions.
func NewSchemaCollisionWarning ¶ added in v1.35.0
func NewSchemaCollisionWarning(schemaName, resolution, section, firstFile, secondFile string, line, col int) *JoinWarning
NewSchemaCollisionWarning creates a warning for schema/definition collisions.
func NewSchemaDedupWarning ¶ added in v1.35.0
func NewSchemaDedupWarning(schemaName, section, sourceFile string, line, col int) *JoinWarning
NewSchemaDedupWarning creates a warning for schema deduplication.
func NewSchemaRenamedWarning ¶ added in v1.35.0
func NewSchemaRenamedWarning(originalName, newName, section, sourceFile string, line, col int, keptOriginal bool) *JoinWarning
NewSchemaRenamedWarning creates a warning when a schema is renamed.
func NewSemanticDedupSummaryWarning ¶ added in v1.35.0
func NewSemanticDedupSummaryWarning(count int, section string) *JoinWarning
NewSemanticDedupSummaryWarning creates a summary warning for semantic deduplication.
func NewVersionMismatchWarning ¶ added in v1.35.0
func NewVersionMismatchWarning(file1, version1, file2, version2, targetVersion string) *JoinWarning
NewVersionMismatchWarning creates a warning for version mismatches.
func NewWebhookCollisionWarning ¶ added in v1.35.0
func NewWebhookCollisionWarning(webhookName, resolution, firstFile, secondFile string, line, col int) *JoinWarning
NewWebhookCollisionWarning creates a warning for webhook collisions.
func (*JoinWarning) HasLocation ¶ added in v1.35.0
func (w *JoinWarning) HasLocation() bool
HasLocation returns true if source location information is available.
func (*JoinWarning) Location ¶ added in v1.35.0
func (w *JoinWarning) Location() string
Location returns an IDE-friendly location string.
func (*JoinWarning) String ¶ added in v1.35.0
func (w *JoinWarning) String() string
String returns a formatted warning message. For most warnings, Context is included in Message via the constructor functions. This method returns just the Message for simplicity and backward compatibility.
type JoinWarnings ¶ added in v1.35.0
type JoinWarnings []*JoinWarning
JoinWarnings is a collection of JoinWarning.
func (JoinWarnings) ByCategory ¶ added in v1.35.0
func (ws JoinWarnings) ByCategory(cat WarningCategory) JoinWarnings
ByCategory filters warnings by category.
func (JoinWarnings) BySeverity ¶ added in v1.35.0
func (ws JoinWarnings) BySeverity(sev severity.Severity) JoinWarnings
BySeverity filters warnings by severity.
func (JoinWarnings) Strings ¶ added in v1.35.0
func (ws JoinWarnings) Strings() []string
Strings returns warning messages for backward compatibility.
func (JoinWarnings) Summary ¶ added in v1.35.0
func (ws JoinWarnings) Summary() string
Summary returns a formatted summary of warnings.
type Joiner ¶
type Joiner struct {
// SourceMaps maps source file paths to their SourceMaps for location lookup.
// When populated, collision errors and events include line/column information.
SourceMaps map[string]*parser.SourceMap
// contains filtered or unexported fields
}
Joiner handles joining of multiple OpenAPI specifications.
Concurrency: Joiner instances are not safe for concurrent use. Create separate Joiner instances for concurrent operations.
func New ¶
func New(config JoinerConfig) *Joiner
New creates a new Joiner instance with the provided configuration
func (*Joiner) Join ¶
func (j *Joiner) Join(specPaths []string) (*JoinResult, error)
Join joins multiple OpenAPI specifications into a single document
func (*Joiner) JoinParsed ¶ added in v1.3.1
func (j *Joiner) JoinParsed(parsedDocs []parser.ParseResult) (*JoinResult, error)
func (*Joiner) WriteResult ¶
func (j *Joiner) WriteResult(result *JoinResult, outputPath string) error
WriteResult writes a join result to a file in YAML or JSON format (matching the source format)
The output file is written with restrictive permissions (0600 - owner read/write only) to protect potentially sensitive API specifications. If the file already exists, its permissions will be explicitly set to 0600 after writing.
type JoinerConfig ¶
type JoinerConfig struct {
// DefaultStrategy is the global strategy for all collisions
DefaultStrategy CollisionStrategy
// PathStrategy defines strategy specifically for path collisions
PathStrategy CollisionStrategy
// SchemaStrategy defines strategy specifically for schema/definition collisions
SchemaStrategy CollisionStrategy
// ComponentStrategy defines strategy for other component collisions (parameters, responses, etc.)
ComponentStrategy CollisionStrategy
// DeduplicateTags removes duplicate tags by name
DeduplicateTags bool
// MergeArrays determines whether to merge array fields (servers, security, etc.)
MergeArrays bool
// Advanced collision strategies configuration
// RenameTemplate is a Go template for renamed schema names (default: "{{.Name}}_{{.Source}}")
// Available variables: {{.Name}} (original name), {{.Source}} (source file), {{.Index}} (doc index)
RenameTemplate string
// NamespacePrefix maps source file paths to namespace prefixes for schema names
// Example: {"users-api.yaml": "Users", "billing-api.yaml": "Billing"}
// When a prefix is configured, schemas from that source get prefixed: User -> Users_User
NamespacePrefix map[string]string
// AlwaysApplyPrefix when true applies namespace prefix to all schemas from a source,
// not just those that collide. When false (default), prefix is only applied on collision.
AlwaysApplyPrefix bool
// EquivalenceMode controls depth of schema comparison: "none", "shallow", or "deep"
EquivalenceMode string
// CollisionReport enables detailed collision analysis reporting
CollisionReport bool
// SemanticDeduplication enables cross-document schema deduplication after merging.
// When enabled, semantically identical schemas are consolidated to a single
// canonical schema (alphabetically first), and all references are rewritten.
SemanticDeduplication bool
// OperationContext enables operation-aware context in rename templates.
// When true, builds a reference graph to populate Path, Method, OperationID,
// Tags, and other operation-derived fields in the rename context.
// Default: false.
OperationContext bool
// PrimaryOperationPolicy determines which operation provides primary context
// when a schema is referenced by multiple operations.
// Default: PolicyFirstEncountered.
PrimaryOperationPolicy PrimaryOperationPolicy
}
JoinerConfig configures how documents are joined
func DefaultConfig ¶
func DefaultConfig() JoinerConfig
DefaultConfig returns a sensible default configuration
type OperationRef ¶ added in v1.39.0
type OperationRef struct {
Path string // API path: "/users/{id}"
Method string // HTTP method: "get", "post", "delete"
OperationID string // Operation identifier if defined
Tags []string // Tags associated with the operation
UsageType UsageType // Where the schema is referenced
StatusCode string // Response status code (for response usage)
ParamName string // Parameter name (for parameter usage)
MediaType string // Media type: "application/json"
}
OperationRef represents a direct reference from an operation to a schema.
type Option ¶ added in v1.11.0
type Option func(*joinConfig) error
Option is a function that configures a join operation
func WithAlwaysApplyPrefix ¶ added in v1.23.0
WithAlwaysApplyPrefix enables or disables applying namespace prefix to all schemas, not just those that collide. When false (default), prefix is only applied on collision.
func WithCollisionReport ¶ added in v1.23.0
WithCollisionReport enables or disables detailed collision reporting Default: false
func WithComponentStrategy ¶ added in v1.11.0
func WithComponentStrategy(strategy CollisionStrategy) Option
WithComponentStrategy sets the collision strategy for components
func WithConfig ¶ added in v1.11.0
func WithConfig(config JoinerConfig) Option
WithConfig applies an entire JoinerConfig struct This is useful for reusing existing configurations or loading from files
func WithDeduplicateTags ¶ added in v1.11.0
WithDeduplicateTags enables or disables tag deduplication Default: true
func WithDefaultStrategy ¶ added in v1.11.0
func WithDefaultStrategy(strategy CollisionStrategy) Option
WithDefaultStrategy sets the global collision strategy
func WithEquivalenceMode ¶ added in v1.23.0
WithEquivalenceMode sets the schema comparison mode for deduplication Valid values: "none", "shallow", "deep" Default: "none"
func WithFilePaths ¶ added in v1.11.0
WithFilePaths specifies file paths as input sources
func WithMergeArrays ¶ added in v1.11.0
WithMergeArrays enables or disables array merging (servers, security, etc.) Default: true
func WithNamespacePrefix ¶ added in v1.23.0
WithNamespacePrefix adds a namespace prefix mapping for a source file. When schemas from a source file collide (or when AlwaysApplyPrefix is true), the prefix is applied to schema names: e.g., "User" -> "Users_User" Can be called multiple times to add multiple mappings.
func WithOperationContext ¶ added in v1.39.0
WithOperationContext enables operation-aware context in rename templates. When enabled, the joiner builds a reference graph for each document to populate operation-derived fields like Path, Method, OperationID, and Tags. This enables templates like "{{.OperationID | pascalCase}}{{.Name}}".
Limitation: Only schemas from the RIGHT (incoming) document receive operation context. The LEFT (base) document's schemas do not have their references traced, so RenameContext fields like Path, Method, OperationID, and Tags will be empty for base document schemas.
func WithParsed ¶ added in v1.11.0
func WithParsed(docs ...parser.ParseResult) Option
WithParsed specifies parsed ParseResults as input sources
func WithPathStrategy ¶ added in v1.11.0
func WithPathStrategy(strategy CollisionStrategy) Option
WithPathStrategy sets the collision strategy for paths
func WithPostJoinOverlay ¶ added in v1.24.0
WithPostJoinOverlay sets an overlay to be applied after joining is complete.
Only one post-join overlay can be specified (last one wins). This is useful for adding unified metadata, removing internal extensions, or applying final transformations to the merged document.
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("api1.yaml", "api2.yaml"),
joiner.WithPostJoinOverlay(enhanceOverlay),
)
func WithPostJoinOverlayFile ¶ added in v1.24.0
WithPostJoinOverlayFile sets an overlay file to be applied after joining is complete.
This is a convenience wrapper around WithPostJoinOverlay that parses the overlay file. Only one post-join overlay file can be specified (last one wins).
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("api1.yaml", "api2.yaml"),
joiner.WithPostJoinOverlayFile("enhance.yaml"),
)
func WithPreJoinOverlay ¶ added in v1.24.0
WithPreJoinOverlay adds an overlay to be applied to all input specs before joining.
Multiple pre-join overlays can be specified and are applied in order. This is useful for normalizing specs before merge (e.g., adding required fields, standardizing naming conventions).
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("api1.yaml", "api2.yaml"),
joiner.WithPreJoinOverlay(normalizeOverlay),
)
func WithPreJoinOverlayFile ¶ added in v1.24.0
WithPreJoinOverlayFile adds an overlay file to be applied to all input specs before joining.
This is a convenience wrapper around WithPreJoinOverlay that parses the overlay file. Multiple pre-join overlay files can be specified.
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("api1.yaml", "api2.yaml"),
joiner.WithPreJoinOverlayFile("normalize.yaml"),
)
func WithPrimaryOperationPolicy ¶ added in v1.39.0
func WithPrimaryOperationPolicy(policy PrimaryOperationPolicy) Option
WithPrimaryOperationPolicy sets the policy for selecting the primary operation when a schema is referenced by multiple operations. The primary operation's context is used for the single-value fields (Path, Method, OperationID, Tags). Aggregate fields (AllPaths, AllMethods, etc.) always contain all references.
func WithRenameTemplate ¶ added in v1.23.0
WithRenameTemplate sets the Go template for renamed schema names Default: "{{.Name}}_{{.Source}}" Available variables: {{.Name}}, {{.Source}}, {{.Index}}, {{.Suffix}}
func WithSchemaStrategy ¶ added in v1.11.0
func WithSchemaStrategy(strategy CollisionStrategy) Option
WithSchemaStrategy sets the collision strategy for schemas/definitions
func WithSemanticDeduplication ¶ added in v1.26.0
WithSemanticDeduplication enables or disables semantic schema deduplication. When enabled, after merging all documents, the joiner identifies semantically identical schemas and consolidates them to a single canonical schema. The canonical name is selected alphabetically (e.g., "Address" beats "Location"). All references to duplicate schemas are rewritten to the canonical name. Default: false
func WithSourceMaps ¶ added in v1.27.0
WithSourceMaps provides SourceMaps for all input documents. The map keys should match the file paths used when parsing (e.g., ParseResult.SourcePath). When provided, collision errors and events include line/column information for both sides of the collision, enabling precise error reporting.
func WithSpecOverlay ¶ added in v1.24.0
WithSpecOverlay maps a specific overlay to a specific input spec.
The specIdentifier should match either:
- A file path from WithFilePaths (e.g., "api1.yaml")
- An index like "0", "1", etc. for WithParsed documents
This allows applying different transformations to different input specs.
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("users-api.yaml", "billing-api.yaml"),
joiner.WithSpecOverlay("users-api.yaml", usersOverlay),
joiner.WithSpecOverlay("billing-api.yaml", billingOverlay),
)
func WithSpecOverlayFile ¶ added in v1.24.0
WithSpecOverlayFile maps a specific overlay file to a specific input spec.
This is a convenience wrapper around WithSpecOverlay that parses the overlay file.
Example:
result, err := joiner.JoinWithOptions(
joiner.WithFilePaths("users-api.yaml", "billing-api.yaml"),
joiner.WithSpecOverlayFile("users-api.yaml", "users-overlay.yaml"),
)
type PrimaryOperationPolicy ¶ added in v1.39.0
type PrimaryOperationPolicy int
PrimaryOperationPolicy determines which operation provides primary context when a schema is referenced by multiple operations.
const ( // PolicyFirstEncountered uses the first operation found during traversal. PolicyFirstEncountered PrimaryOperationPolicy = iota // PolicyMostSpecific prefers operations with operationId, then tags. PolicyMostSpecific // PolicyAlphabetical sorts by path+method and uses alphabetically first. PolicyAlphabetical )
type RefGraph ¶ added in v1.39.0
type RefGraph struct {
// contains filtered or unexported fields
}
RefGraph represents the complete reference structure of an OpenAPI document. It enables traversal from any schema to the operations that reference it, either directly or through intermediate schemas.
The full implementation is in refgraph.go.
func (*RefGraph) ResolveLineage ¶ added in v1.39.0
func (g *RefGraph) ResolveLineage(schemaName string) []OperationRef
ResolveLineage returns all operations that reference the given schema, either directly or through intermediate schemas.
type RenameContext ¶ added in v1.39.0
type RenameContext struct {
// Core fields (backward compatible with renameTemplateData)
Name string // Original schema name
Source string // Source file name (sanitized)
Index int // Document index (0-based)
// Operation context (from primary operation reference)
Path string // API path: "/users/{id}"
Method string // HTTP method: "get", "post"
OperationID string // Operation ID if defined
Tags []string // Tags from primary operation
UsageType UsageType // request, response, parameter, header, callback
StatusCode string // Response status code
ParamName string // Parameter name
MediaType string // Media type
// Aggregate context (when schema has multiple operation references)
AllPaths []string // All referencing paths
AllMethods []string // All methods (deduplicated)
AllOperationIDs []string // All operation IDs (non-empty only)
AllTags []string // All tags (deduplicated)
RefCount int // Total operation references
PrimaryResource string // Extracted resource name from path
}
RenameContext provides comprehensive context for schema renaming decisions. It extends the original renameTemplateData with operation-derived fields.
type SchemaDifference ¶ added in v1.23.0
type SchemaDifference struct {
Path string // JSON path to differing element (e.g., "properties.name.type")
LeftValue any
LeftLine int // 1-based line number for left value (0 if unknown)
LeftColumn int // 1-based column number for left value (0 if unknown)
RightValue any
RightLine int // 1-based line number for right value (0 if unknown)
RightColumn int // 1-based column number for right value (0 if unknown)
Description string
}
SchemaDifference describes a structural difference between two schemas
type SchemaRef ¶ added in v1.39.0
type SchemaRef struct {
FromSchema string // The schema containing the $ref
RefLocation string // Where in the schema: "properties.address", "items", "allOf[0]"
}
SchemaRef represents a reference from one schema to another.
type SchemaRewriter ¶ added in v1.23.0
type SchemaRewriter struct {
// contains filtered or unexported fields
}
SchemaRewriter handles rewriting of schema references throughout an OpenAPI document
func NewSchemaRewriter ¶ added in v1.23.0
func NewSchemaRewriter() *SchemaRewriter
NewSchemaRewriter creates a new rewriter instance
func (*SchemaRewriter) RegisterRename ¶ added in v1.23.0
func (r *SchemaRewriter) RegisterRename(oldName, newName string, version parser.OASVersion)
RegisterRename registers a schema rename operation
func (*SchemaRewriter) RewriteDocument ¶ added in v1.23.0
func (r *SchemaRewriter) RewriteDocument(doc any) error
RewriteDocument traverses and rewrites all references in the document
type UsageType ¶ added in v1.39.0
type UsageType string
UsageType indicates where a schema is referenced within an operation.
const ( // UsageTypeRequest indicates the schema is used in a request body. UsageTypeRequest UsageType = "request" // UsageTypeResponse indicates the schema is used in a response body. UsageTypeResponse UsageType = "response" // UsageTypeParameter indicates the schema is used in a parameter. UsageTypeParameter UsageType = "parameter" // UsageTypeHeader indicates the schema is used in a header. UsageTypeHeader UsageType = "header" // UsageTypeCallback indicates the schema is used in a callback. UsageTypeCallback UsageType = "callback" )
type WarningCategory ¶ added in v1.35.0
type WarningCategory string
WarningCategory identifies the type of warning.
const ( // WarnVersionMismatch indicates documents have different minor versions. WarnVersionMismatch WarningCategory = "version_mismatch" // WarnPathCollision indicates a path collision was resolved. WarnPathCollision WarningCategory = "path_collision" // WarnSchemaCollision indicates a schema/definition collision was resolved. WarnSchemaCollision WarningCategory = "schema_collision" // WarnWebhookCollision indicates a webhook collision was resolved. WarnWebhookCollision WarningCategory = "webhook_collision" // WarnSchemaRenamed indicates a schema was renamed due to collision. WarnSchemaRenamed WarningCategory = "schema_renamed" // WarnSchemaDeduplicated indicates a schema was deduplicated. WarnSchemaDeduplicated WarningCategory = "schema_deduplicated" // WarnNamespacePrefixed indicates a namespace prefix was applied. WarnNamespacePrefixed WarningCategory = "namespace_prefixed" // WarnMetadataOverride indicates metadata was overridden (host, basePath). WarnMetadataOverride WarningCategory = "metadata_override" // WarnSemanticDedup indicates semantic deduplication summary. WarnSemanticDedup WarningCategory = "semantic_dedup" // WarnGenericSourceName indicates a document has a generic or empty source name. // This makes collision reports less useful for identifying which document caused the collision. WarnGenericSourceName WarningCategory = "generic_source_name" )