converter

package
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Nov 16, 2025 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package converter provides OpenAPI Specification (OAS) version conversion functionality.

This package converts OpenAPI specifications between different OAS versions while tracking conversion issues and incompatibilities. It performs best-effort conversion to preserve maximum information while clearly documenting any lossy transformations or features that cannot be converted.

Supported Conversions

The converter supports the following conversion paths:

  • OAS 2.0 (Swagger) → OAS 3.x (3.0.0 through 3.2.0)
  • OAS 3.x → OAS 2.0
  • OAS 3.x → OAS 3.y (version updates within 3.x family)

All conversions are designed to be lossless where possible, but some features are specific to certain versions and cannot be fully converted. The converter tracks all such limitations as ConversionIssue items with appropriate severity levels.

Features

  • Multi-version support: Convert between OAS 2.0 and all OAS 3.x versions
  • Best-effort conversion: Preserves maximum information while documenting limitations
  • Issue tracking: Detailed reporting of conversion issues with severity levels
  • Schema conversion: Handles JSON schema differences between versions
  • Security scheme conversion: Converts between different security definition formats
  • Dual API pattern: Convenience functions and reusable converter instances

Conversion Severity Levels

The converter provides three severity levels for issues:

  • SeverityInfo: Informational messages about conversion choices
  • SeverityWarning: Lossy conversions or best-effort transformations
  • SeverityCritical: Features that cannot be converted (data loss)

Critical issues indicate features that exist in the source version but have no equivalent in the target version. Warnings indicate lossy conversions where some information may be lost but a reasonable approximation can be made. Info messages provide additional context about conversion choices.

OAS 2.0 to OAS 3.x Conversion

When converting from OAS 2.0 to OAS 3.x, the following transformations occur:

Automatic Conversions:

  • swagger/host/basePath/schemes → servers array
  • definitions → components.schemas
  • parameters → components.parameters
  • responses → components.responses
  • securityDefinitions → components.securitySchemes
  • consumes/produces → requestBody.content and responses.content
  • Body parameters → requestBody objects
  • OAuth2 flows → OAS 3.x flow objects
  • Basic authentication → HTTP security scheme

Known Limitations:

  • collectionFormat values may not map perfectly to style/explode parameters (Warning)
  • allowEmptyValue was removed in OAS 3.0 (Warning)
  • Some OAuth2 flow names changed between versions (converted automatically)

OAS 3.x to OAS 2.0 Conversion

When converting from OAS 3.x to OAS 2.0, the following transformations occur:

Automatic Conversions:

  • First server → host/basePath/schemes
  • components.schemas → definitions
  • components.parameters → parameters
  • components.responses → responses
  • components.securitySchemes → securityDefinitions
  • requestBody → body parameter with schema
  • Media types from content → consumes/produces arrays
  • HTTP basic scheme → basic authentication
  • OAuth2 flows → single OAuth2 flow

Known Limitations (Critical Issues):

  • Webhooks cannot be represented in OAS 2.0 (OAS 3.1+)
  • Callbacks cannot be converted
  • Links cannot be converted
  • TRACE HTTP method is not supported
  • Cookie parameters (in: cookie) not supported in OAS 2.0
  • OpenID Connect security schemes not supported
  • Multiple servers (only first server is converted)

Known Limitations (Warnings):

  • Multiple media types in requestBody (uses first, lists others in consumes)
  • Multiple OAuth2 flows (uses first, ignores others)
  • Non-basic HTTP authentication schemes
  • Server variables are removed
  • Style/explode parameter settings
  • Nullable schemas (OAS 3.0+)

OAS 3.x to OAS 3.y Conversion

When converting between OAS 3.x versions (e.g., 3.0.3 to 3.1.0), the converter primarily updates the version string. While the 3.x family is generally compatible, minor versions can introduce new features:

  • 3.1.0 added: webhooks, better JSON Schema alignment
  • 3.2.0 added: additional features (check spec for details)

The converter adds an informational message about the version update and reminds users to verify that features used are supported in the target version.

Basic Usage

For simple, one-off conversion, use the convenience function:

result, err := converter.Convert("swagger.yaml", "3.0.3")
if err != nil {
	log.Fatalf("Conversion failed: %v", err)
}

if result.HasCriticalIssues() {
	fmt.Printf("Conversion completed with %d critical issue(s):\n", result.CriticalCount)
	for _, issue := range result.Issues {
		if issue.Severity == converter.SeverityCritical {
			fmt.Printf("  %s\n", issue.String())
		}
	}
}

For converting multiple files with the same configuration, create a Converter instance:

c := converter.New()
c.StrictMode = false
c.IncludeInfo = true

result1, err := c.Convert("api1.yaml", "3.0.3")
result2, err := c.Convert("api2.yaml", "3.0.3")

Advanced Usage

Enable strict mode to fail on any issues (even warnings):

c := converter.New()
c.StrictMode = true
result, err := c.Convert("swagger.yaml", "3.0.3")
if err != nil {
	// err will be non-nil if there are any warnings or critical issues
	log.Fatalf("Strict conversion failed: %v", err)
}

Convert an already-parsed document:

parseResult, _ := parser.Parse("openapi.yaml", false, true)
result, err := converter.ConvertParsed(*parseResult, "2.0")
if err != nil {
	log.Fatalf("Conversion failed: %v", err)
}

Suppress informational messages:

c := converter.New()
c.IncludeInfo = false
result, err := c.Convert("swagger.yaml", "3.0.3")
// result.Issues will only contain warnings and critical issues

Issue Reporting

The ConversionResult contains detailed information about all issues encountered:

for _, issue := range result.Issues {
	fmt.Printf("[%s] %s: %s\n", issue.Severity, issue.Path, issue.Message)
	if issue.Context != "" {
		fmt.Printf("    Context: %s\n", issue.Context)
	}
}

Issues are categorized by severity:

fmt.Printf("Conversion summary:\n")
fmt.Printf("  Info:     %d\n", result.InfoCount)
fmt.Printf("  Warnings: %d\n", result.WarningCount)
fmt.Printf("  Critical: %d\n", result.CriticalCount)

Writing Converted Documents

Currently, converter doesn't include a WriteResult method. Use YAML marshal from the yaml.v3 package:

import "gopkg.in/yaml.v3"

data, err := yaml.Marshal(result.Document)
if err != nil {
	log.Fatalf("Failed to marshal: %v", err)
}
os.WriteFile("converted.yaml", data, 0600)

Validation After Conversion

Always validate the converted document to ensure it's valid for the target version:

// Convert the document
convResult, err := converter.Convert("swagger.yaml", "3.0.3")
if err != nil {
	log.Fatal(err)
}

// Write to temporary file
tmpFile := "temp-converted.yaml"
data, _ := yaml.Marshal(convResult.Document)
os.WriteFile(tmpFile, data, 0600)

// Validate the converted document
valResult, err := validator.Validate(tmpFile, true, false)
if err != nil {
	log.Fatal(err)
}
if !valResult.Valid {
	fmt.Printf("Converted document has validation errors:\n")
	for _, err := range valResult.Errors {
		fmt.Printf("  %s\n", err.String())
	}
}

Performance Notes

The converter performs deep copying of document structures to avoid mutations. Performance considerations:

  • Large documents: Deep copying is O(n) in document size
  • Complex schemas: Nested schema conversion requires traversal
  • Multiple conversions: Reuse Converter instances for better performance

For better performance:

  • Convert documents during build/deployment rather than runtime
  • Reuse Converter instances when converting multiple documents
  • Disable info messages (IncludeInfo: false) if not needed
  • Cache conversion results for unchanged documents

Conversion Result Format

The ConversionResult contains:

  • Document: The converted document (*parser.OAS2Document or *parser.OAS3Document)
  • SourceVersion: Detected source OAS version string
  • SourceOASVersion: Enumerated source OAS version
  • TargetVersion: Target OAS version string
  • TargetOASVersion: Enumerated target OAS version
  • Issues: All conversion issues with paths and descriptions
  • InfoCount: Number of informational messages
  • WarningCount: Number of warnings
  • CriticalCount: Number of critical issues
  • Success: True if conversion completed without critical issues

Each ConversionIssue contains:

  • Path: JSON path to the affected element (e.g., "paths./pets.get.parameters[0]")
  • Message: Human-readable description of the issue
  • Severity: Info, Warning, or Critical
  • Field: Specific field name (optional)
  • Value: The problematic value (optional)
  • Context: Additional context or suggestions (optional)

Common Conversion Issues

Critical issues (features that cannot be converted):

  • "Webhooks are OAS 3.1+ only and cannot be converted to OAS 2.0"
  • "Operation contains callbacks which are not supported in OAS 2.0"
  • "Response contains links which are not supported in OAS 2.0"
  • "Cookie parameters are not supported in OAS 2.0"
  • "TRACE method is OAS 3.x only and cannot be converted to OAS 2.0"
  • "OpenID Connect is OAS 3.x only and cannot be converted to OAS 2.0"

Warnings (lossy conversions):

  • "Multiple servers defined (N), using only the first one"
  • "RequestBody has multiple media types (N), using first (application/json)"
  • "Multiple OAuth2 flows defined (N), using only one"
  • "Parameter uses collectionFormat 'X'"
  • "Parameter uses 'allowEmptyValue'"
  • "Schema uses 'nullable' which is OAS 3.0+"
  • "Server variables are not supported in OAS 2.0"

Info messages (conversions choices):

  • "No host specified in OAS 2.0 document, using default server"
  • "No servers defined in OAS 3.x document, using defaults"
  • "Updated version from X to Y"
  • "Source and target versions are the same (X), no conversion needed"

Limitations

  • External references: The converter preserves $ref but does not follow or convert referenced documents
  • Custom extensions: Extension fields (x-*) are preserved but not validated or converted
  • Complex schemas: Some advanced JSON Schema keywords may not convert perfectly
  • Specification evolution: Future OAS versions may introduce breaking changes requiring updates

Best Practices

  1. Always validate converted documents using the validator package
  2. Review critical issues and warnings before deploying converted specs
  3. Test converted APIs to ensure behavior is preserved
  4. Keep backups of original specifications
  5. Use version control to track conversion history
  6. Document any manual adjustments made after conversion
  7. Consider the target version's feature set before converting

Version Selection

When choosing a target version:

  • Converting to 3.0.3: Most compatible with older tools and libraries
  • Converting to 3.1.x: Better JSON Schema alignment, adds webhooks
  • Converting to 2.0: For legacy tools that only support Swagger 2.0

Error Handling

The converter returns errors for:

  • Invalid source documents (parse errors)
  • Invalid target version strings
  • Unsupported conversion paths
  • Document type mismatches
  • Strict mode failures (when warnings/critical issues exist)

The converter does NOT return errors for:

  • Conversion issues (tracked in ConversionResult.Issues)
  • Lossy conversions (tracked as warnings)
  • Feature incompatibilities (tracked as critical issues)

Check ConversionResult.Success and ConversionResult.Issues for conversion outcomes rather than relying solely on the error return value.

Example

Example demonstrates basic conversion using the convenience function

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	// Convert an OAS 2.0 specification to OAS 3.0.3
	result, err := converter.Convert("testdata/petstore-2.0.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	// Check for critical issues
	if result.HasCriticalIssues() {
		fmt.Printf("Conversion completed with %d critical issue(s)\n", result.CriticalCount)
		return
	}

	fmt.Printf("Successfully converted from %s to %s\n", result.SourceVersion, result.TargetVersion)
	fmt.Printf("Issues: %d info, %d warnings, %d critical\n",
		result.InfoCount, result.WarningCount, result.CriticalCount)
}
Example (ConvertOAS2ToOAS3)

Example_convertOAS2ToOAS3 demonstrates converting from OAS 2.0 to OAS 3.x

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, err := converter.Convert("swagger-2.0.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Converted OAS 2.0 to OAS 3.0.3\n")
	fmt.Printf("Success: %v\n", result.Success)
	fmt.Printf("Issues: %d info, %d warnings, %d critical\n",
		result.InfoCount, result.WarningCount, result.CriticalCount)
}
Example (ConvertOAS3ToOAS2)

Example_convertOAS3ToOAS2 demonstrates converting from OAS 3.x to OAS 2.0

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, err := converter.Convert("openapi-3.0.yaml", "2.0")
	if err != nil {
		log.Fatal(err)
	}

	if result.HasCriticalIssues() {
		fmt.Println("Warning: Some OAS 3.x features could not be converted to OAS 2.0")
		fmt.Printf("Critical issues: %d\n", result.CriticalCount)
	}

	fmt.Printf("Converted OAS 3.x to OAS 2.0\n")
	fmt.Printf("Review the %d issue(s) before using the converted document\n", len(result.Issues))
}
Example (HandleConversionIssues)

Example_handleConversionIssues demonstrates processing conversion issues

package main

import (
	"fmt"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, _ := converter.Convert("openapi.yaml", "2.0")

	// Categorize issues by severity
	for _, issue := range result.Issues {
		switch issue.Severity {
		case converter.SeverityCritical:
			fmt.Printf("CRITICAL [%s]: %s\n", issue.Path, issue.Message)
			if issue.Context != "" {
				fmt.Printf("  Context: %s\n", issue.Context)
			}
		case converter.SeverityWarning:
			fmt.Printf("WARNING [%s]: %s\n", issue.Path, issue.Message)
		case converter.SeverityInfo:
			fmt.Printf("INFO [%s]: %s\n", issue.Path, issue.Message)
		}
	}

	// Summary
	fmt.Printf("\nSummary: %d critical, %d warnings, %d info\n",
		result.CriticalCount, result.WarningCount, result.InfoCount)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ConversionIssue

type ConversionIssue struct {
	// Path is the JSON path to the field (e.g., "paths./pets.get.parameters[0]")
	Path string
	// Message is a human-readable description
	Message string
	// Severity indicates the severity level
	Severity Severity
	// Field is the specific field name that has the issue
	Field string
	// Value is the problematic value (optional)
	Value interface{}
	// Context provides additional information about the issue
	Context string
}

ConversionIssue represents a single conversion issue or limitation

func (ConversionIssue) String

func (i ConversionIssue) String() string

String returns a formatted string representation of the conversion issue

Example

ExampleConversionIssue_String demonstrates formatting conversion issues

package main

import (
	"fmt"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, _ := converter.Convert("openapi.yaml", "2.0")

	// Print all issues with formatted output
	for _, issue := range result.Issues {
		fmt.Println(issue.String())
	}
}

type ConversionResult

type ConversionResult struct {
	// Document contains the converted document (*parser.OAS2Document or *parser.OAS3Document)
	Document interface{}
	// SourceVersion is the detected source OAS version string
	SourceVersion string
	// SourceOASVersion is the enumerated source OAS version
	SourceOASVersion parser.OASVersion
	// TargetVersion is the target OAS version string
	TargetVersion string
	// TargetOASVersion is the enumerated target OAS version
	TargetOASVersion parser.OASVersion
	// Issues contains all conversion issues grouped by severity
	Issues []ConversionIssue
	// InfoCount is the total number of info messages
	InfoCount int
	// WarningCount is the total number of warnings
	WarningCount int
	// CriticalCount is the total number of critical issues
	CriticalCount int
	// Success is true if conversion completed without critical issues
	Success bool
}

ConversionResult contains the results of converting an OpenAPI specification

func Convert

func Convert(specPath string, targetVersion string) (*ConversionResult, error)

Convert is a convenience function that converts an OpenAPI specification file to a target version with the specified options. It's equivalent to creating a Converter with New() and calling Convert().

For one-off conversion operations, this function provides a simpler API. For converting multiple files with the same configuration, create a Converter instance and reuse it.

Example:

result, err := converter.Convert("swagger.yaml", "3.0.3")
if err != nil {
    log.Fatal(err)
}
if result.HasCriticalIssues() {
    // Handle critical issues
}
Example

ExampleConvert demonstrates the package-level convenience function

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, err := converter.Convert("swagger.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	if result.Success {
		fmt.Println("Conversion successful")
	} else {
		fmt.Printf("Conversion failed with %d critical issues\n", result.CriticalCount)
	}
}

func ConvertParsed

func ConvertParsed(parseResult parser.ParseResult, targetVersion string) (*ConversionResult, error)

ConvertParsed is a convenience function that converts an already-parsed OpenAPI specification to a target version.

Example:

parseResult, _ := parser.Parse("swagger.yaml", false, true)
result, err := converter.ConvertParsed(*parseResult, "3.0.3")
Example

ExampleConvertParsed demonstrates converting an already-parsed document

package main

import (
	"fmt"
	"log"

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

func main() {
	// Parse the document first
	parseResult, err := parser.Parse("openapi.yaml", false, true)
	if err != nil {
		log.Fatal(err)
	}

	// Convert the parsed document
	result, err := converter.ConvertParsed(*parseResult, "2.0")
	if err != nil {
		log.Fatal(err)
	}

	if result.HasCriticalIssues() {
		fmt.Printf("Critical issues found: %d\n", result.CriticalCount)
		for _, issue := range result.Issues {
			if issue.Severity == converter.SeverityCritical {
				fmt.Printf("  - %s: %s\n", issue.Path, issue.Message)
			}
		}
	}
}

func (*ConversionResult) HasCriticalIssues

func (r *ConversionResult) HasCriticalIssues() bool

HasCriticalIssues returns true if there are any critical issues

Example

ExampleConversionResult_HasCriticalIssues demonstrates checking for critical issues

package main

import (
	"fmt"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, _ := converter.Convert("openapi-3.1.yaml", "2.0")

	if result.HasCriticalIssues() {
		fmt.Printf("Found %d critical issues:\n", result.CriticalCount)
		for _, issue := range result.Issues {
			if issue.Severity == converter.SeverityCritical {
				fmt.Printf("  %s\n", issue.String())
			}
		}
	} else {
		fmt.Println("No critical issues")
	}
}

func (*ConversionResult) HasWarnings

func (r *ConversionResult) HasWarnings() bool

HasWarnings returns true if there are any warnings

Example

ExampleConversionResult_HasWarnings demonstrates checking for warnings

package main

import (
	"fmt"

	"github.com/erraggy/oastools/converter"
)

func main() {
	result, _ := converter.Convert("swagger.yaml", "3.0.3")

	if result.HasWarnings() {
		fmt.Printf("Found %d warnings\n", result.WarningCount)
	} else {
		fmt.Println("No warnings")
	}
}

type Converter

type Converter struct {
	// StrictMode causes conversion to fail on any issues (even warnings)
	StrictMode bool
	// IncludeInfo determines whether to include informational messages
	IncludeInfo bool
}

Converter handles OpenAPI specification version conversion

Example

ExampleConverter demonstrates using a reusable Converter instance

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	// Create a converter with custom settings
	c := converter.New()
	c.StrictMode = false
	c.IncludeInfo = true

	// Convert multiple files with the same settings
	result1, err := c.Convert("api-v2-1.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	result2, err := c.Convert("api-v2-2.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Converted %d documents\n", 2)
	fmt.Printf("Result 1: Success=%v, Issues=%d\n", result1.Success, len(result1.Issues))
	fmt.Printf("Result 2: Success=%v, Issues=%d\n", result2.Success, len(result2.Issues))
}
Example (StrictMode)

ExampleConverter_strictMode demonstrates strict mode behavior

package main

import (
	"fmt"

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

func main() {
	c := converter.New()
	c.StrictMode = true // Fail on any issues

	parseResult, _ := parser.Parse("openapi.yaml", false, true)

	result, err := c.ConvertParsed(*parseResult, "2.0")
	if err != nil {
		// In strict mode, errors are returned for any warnings or critical issues
		fmt.Printf("Strict mode conversion failed: %v\n", err)
		fmt.Printf("Issues: %d warnings, %d critical\n", result.WarningCount, result.CriticalCount)
		return
	}

	fmt.Println("Conversion succeeded with no issues")
}

func New

func New() *Converter

New creates a new Converter instance with default settings

func (*Converter) Convert

func (c *Converter) Convert(specPath string, targetVersion string) (*ConversionResult, error)

Convert converts an OpenAPI specification file to a target version

Example

ExampleConverter_Convert demonstrates the instance Convert method

package main

import (
	"fmt"
	"log"

	"github.com/erraggy/oastools/converter"
)

func main() {
	c := converter.New()

	result, err := c.Convert("swagger.yaml", "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Converted %s to %s\n", result.SourceVersion, result.TargetVersion)
}

func (*Converter) ConvertParsed

func (c *Converter) ConvertParsed(parseResult parser.ParseResult, targetVersionStr string) (*ConversionResult, error)

ConvertParsed converts an already-parsed OpenAPI specification to a target version

Example

ExampleConverter_ConvertParsed demonstrates the instance ConvertParsed method

package main

import (
	"fmt"
	"log"

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

func main() {
	c := converter.New()
	c.IncludeInfo = false // Suppress info messages

	parseResult, _ := parser.Parse("openapi.yaml", false, true)

	result, err := c.ConvertParsed(*parseResult, "3.0.3")
	if err != nil {
		log.Fatal(err)
	}

	// Only warnings and critical issues will be included
	fmt.Printf("Warnings: %d, Critical: %d\n", result.WarningCount, result.CriticalCount)
}

type Severity

type Severity int

Severity indicates the severity level of a conversion issue

const (
	// SeverityInfo indicates informational messages about conversion choices
	SeverityInfo Severity = iota
	// SeverityWarning indicates lossy conversions or best-effort transformations
	SeverityWarning
	// SeverityCritical indicates features that cannot be converted (data loss)
	SeverityCritical
)

func (Severity) String

func (s Severity) String() string

Jump to

Keyboard shortcuts

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