overlay

package
v1.7.5 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2025 License: MIT Imports: 14 Imported by: 0

README

OpenAPI

OpenAPI Overlay

An implementation of the OpenAPI Overlay Specification for applying modifications to OpenAPI documents

Overlay reference Built by Speakeasy Release Go Doc
Go Report Card Software License

⚠️ This an alpha implementation. If you'd like to discuss a production use case please join the Speakeasy slack.

Features

  • OpenAPI Overlay Specification Compliance: Full implementation of the OpenAPI Overlay Specification (2023-10-12)
  • JSONPath Target Selection: Uses JSONPath expressions to select nodes for modification
  • Remove and Update Actions: Support for both remove actions (pruning nodes) and update actions (merging values)
  • Flexible Input/Output: Works with both YAML and JSON formats
  • Batch Operations: Apply multiple modifications to large numbers of nodes in a single operation
  • YAML v1.2 Support: Uses gopkg.in/yaml.v3 for YAML v1.2 parsing (superset of JSON)

About OpenAPI Overlays

This specification defines a means of editing an OpenAPI Specification file by applying a list of actions. Each action is either a remove action that prunes nodes or an update that merges a value into nodes. The nodes impacted are selected by a target expression which uses JSONPath.

The specification itself says very little about the input file to be modified or the output file. The presumed intention is that the input and output be an OpenAPI Specification, but that is not required.

In many ways, this is similar to JSONPatch, but without the requirement to use a single explicit path for each operation. This allows the creator of an overlay file to apply a single modification to a large number of nodes in the file within a single operation.

Apply an overlay to an OpenAPI document

Shows loading an overlay specification and applying it to transform an OpenAPI document.

overlayContent := `overlay: 1.0.0
info:
  title: Pet Store Enhancement Overlay
  version: 1.0.0
actions:
  - target: $.info.description
    update: Enhanced pet store API with additional features`

openAPIContent := `openapi: 3.1.0
info:
  title: Pet Store API
  version: 1.0.0
  description: A simple pet store API
paths:
  /pets:
    get:
      summary: List pets
      responses:
        '200':
          description: A list of pets`

overlayFile := "temp_overlay.yaml"
openAPIFile := "temp_openapi.yaml"
if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
	panic(err)
}
if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil {
	panic(err)
}
defer os.Remove(overlayFile)
defer os.Remove(openAPIFile)

overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	panic(err)
}

openAPINode, err := loader.LoadSpecification(openAPIFile)
if err != nil {
	panic(err)
}

err = overlayDoc.ApplyTo(openAPINode)
if err != nil {
	panic(err)
}

// Convert back to YAML string
var buf strings.Builder
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)
err = encoder.Encode(openAPINode)
if err != nil {
	panic(err)
}

fmt.Printf("Transformed document:\n%s", buf.String())

Create an overlay specification programmatically

Shows building an overlay specification with update and remove actions.

// Create update value as yaml.Node
var updateNode yaml.Node
updateNode.SetString("Enhanced API with additional features")

overlayDoc := &overlay.Overlay{
	Version: "1.0.0",
	Info: overlay.Info{
		Title:   "API Enhancement Overlay",
		Version: "1.0.0",
	},
	Actions: []overlay.Action{
		{
			Target: "$.info.description",
			Update: updateNode,
		},
		{
			Target: "$.paths['/deprecated-endpoint']",
			Remove: true,
		},
	},
}

result, err := overlayDoc.ToString()
if err != nil {
	panic(err)
}

fmt.Printf("Overlay specification:\n%s", result)

Parse an overlay specification from a file

Shows loading an overlay file and accessing its properties.

overlayContent := `overlay: 1.0.0
info:
  title: API Modification Overlay
  version: 1.0.0
actions:
  - target: $.info.title
    update: Enhanced Pet Store API
  - target: $.info.version
    update: 2.0.0`

overlayFile := "temp_overlay.yaml"
if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
	panic(err)
}
defer func() { _ = os.Remove(overlayFile) }()

overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	panic(err)
}

fmt.Printf("Overlay Version: %s\n", overlayDoc.Version)
fmt.Printf("Title: %s\n", overlayDoc.Info.Title)
fmt.Printf("Number of Actions: %d\n", len(overlayDoc.Actions))

for i, action := range overlayDoc.Actions {
	fmt.Printf("Action %d Target: %s\n", i+1, action.Target)
}

Validate an overlay specification

Shows loading and validating an overlay specification for correctness.

invalidOverlay := `overlay: 1.0.0
info:
  title: Invalid Overlay
actions:
  - target: $.info.title
    description: Missing update or remove`

overlayFile := "temp_invalid_overlay.yaml"
if err := os.WriteFile(overlayFile, []byte(invalidOverlay), 0644); err != nil {
	panic(err)
}
defer func() { _ = os.Remove(overlayFile) }()

overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	fmt.Printf("Parse error: %s\n", err.Error())
	return
}

validationErr := overlayDoc.Validate()
if validationErr != nil {
	fmt.Println("Validation errors:")
	fmt.Printf("  %s\n", validationErr.Error())
} else {
	fmt.Println("Overlay specification is valid!")
}

Use remove actions in overlays

Shows removing specific paths and properties from an OpenAPI document.

openAPIContent := `openapi: 3.1.0
info:
  title: API
  version: 1.0.0
paths:
  /users:
    get:
      summary: List users
  /users/{id}:
    get:
      summary: Get user
  /admin:
    get:
      summary: Admin endpoint
      deprecated: true`

overlayContent := `overlay: 1.0.0
info:
  title: Cleanup Overlay
  version: 1.0.0
actions:
  - target: $.paths['/admin']
    remove: true`

openAPIFile := "temp_openapi.yaml"
overlayFile := "temp_overlay.yaml"
if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil {
	panic(err)
}
if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
	panic(err)
}
defer func() { _ = os.Remove(openAPIFile) }()
defer func() { _ = os.Remove(overlayFile) }()

overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	panic(err)
}

openAPINode, err := loader.LoadSpecification(openAPIFile)
if err != nil {
	panic(err)
}

err = overlayDoc.ApplyTo(openAPINode)
if err != nil {
	panic(err)
}

var buf strings.Builder
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)
err = encoder.Encode(openAPINode)
if err != nil {
	panic(err)
}

fmt.Printf("Document after removing deprecated endpoint:\n%s", buf.String())

Contributing

This repository is maintained by Speakeasy, but we welcome and encourage contributions from the community to help improve its capabilities and stability.

How to Contribute

  1. Open Issues: Found a bug or have a feature suggestion? Open an issue to describe what you'd like to see changed.

  2. Pull Requests: We welcome pull requests! If you'd like to contribute code:

    • Fork the repository
    • Create a new branch for your feature/fix
    • Submit a PR with a clear description of the changes and any related issues
  3. Feedback: Share your experience using the packages or suggest improvements.

All contributions, whether they're bug reports, feature requests, or code changes, help make this project better for everyone.

Please ensure your contributions adhere to our coding standards and include appropriate tests where applicable.

Documentation

Overview

Example (Applying)

Example_applying demonstrates how to apply an overlay to an OpenAPI document. Shows loading an overlay specification and applying it to transform an OpenAPI document.

// Create temporary files for this example
overlayContent := `overlay: 1.0.0
info:
  title: Pet Store Enhancement Overlay
  version: 1.0.0
actions:
  - target: $.info.description
    update: Enhanced pet store API with additional features`

openAPIContent := `openapi: 3.1.0
info:
  title: Pet Store API
  version: 1.0.0
  description: A simple pet store API
paths:
  /pets:
    get:
      summary: List pets
      responses:
        '200':
          description: A list of pets`

// Write temporary files
overlayFile := "temp_overlay.yaml"
openAPIFile := "temp_openapi.yaml"
if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
	panic(err)
}
if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil {
	panic(err)
}
defer os.Remove(overlayFile)
defer os.Remove(openAPIFile)

// Parse the overlay
overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	panic(err)
}

// Load the OpenAPI document
openAPINode, err := loader.LoadSpecification(openAPIFile)
if err != nil {
	panic(err)
}

// Apply the overlay to the OpenAPI document
err = overlayDoc.ApplyTo(openAPINode)
if err != nil {
	panic(err)
}

// Convert back to YAML string
var buf strings.Builder
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)
err = encoder.Encode(openAPINode)
if err != nil {
	panic(err)
}

fmt.Printf("Transformed document:\n%s", buf.String())
Output:

Transformed document:
openapi: 3.1.0
info:
  title: Pet Store API
  version: 1.0.0
  description: Enhanced pet store API with additional features
paths:
  /pets:
    get:
      summary: List pets
      responses:
        '200':
          description: A list of pets
Example (Creating)

Example_creating demonstrates how to create an overlay specification programmatically. Shows building an overlay specification with update and remove actions.

// Create update value as yaml.Node
var updateNode yaml.Node
updateNode.SetString("Enhanced API with additional features")

// Create an overlay with update and remove actions
overlayDoc := &overlay.Overlay{
	Version: "1.0.0",
	Info: overlay.Info{
		Title:   "API Enhancement Overlay",
		Version: "1.0.0",
	},
	Actions: []overlay.Action{
		{
			Target: "$.info.description",
			Update: updateNode,
		},
		{
			Target: "$.paths['/deprecated-endpoint']",
			Remove: true,
		},
	},
}

result, err := overlayDoc.ToString()
if err != nil {
	panic(err)
}

fmt.Printf("Overlay specification:\n%s", result)
Output:

Overlay specification:
overlay: 1.0.0
info:
  title: API Enhancement Overlay
  version: 1.0.0
actions:
  - target: $.info.description
    update: Enhanced API with additional features
  - target: $.paths['/deprecated-endpoint']
    remove: true
Example (Parsing)

Example_parsing demonstrates how to parse an overlay specification from a file. Shows loading an overlay file and accessing its properties.

package main

import (
	"fmt"
	"os"

	"github.com/speakeasy-api/openapi/overlay"
)

func main() {
	overlayContent := `overlay: 1.0.0
info:
  title: API Modification Overlay
  version: 1.0.0
actions:
  - target: $.info.title
    update: Enhanced Pet Store API
  - target: $.info.version
    update: 2.0.0`

	// Write temporary file
	overlayFile := "temp_overlay.yaml"
	if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
		panic(err)
	}
	defer func() { _ = os.Remove(overlayFile) }()

	overlayDoc, err := overlay.Parse(overlayFile)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Overlay Version: %s\n", overlayDoc.Version)
	fmt.Printf("Title: %s\n", overlayDoc.Info.Title)
	fmt.Printf("Number of Actions: %d\n", len(overlayDoc.Actions))

	for i, action := range overlayDoc.Actions {
		fmt.Printf("Action %d Target: %s\n", i+1, action.Target)
	}
}
Output:

Overlay Version: 1.0.0
Title: API Modification Overlay
Number of Actions: 2
Action 1 Target: $.info.title
Action 2 Target: $.info.version
Example (Removing)

Example_removing demonstrates how to use remove actions in overlays. Shows removing specific paths and properties from an OpenAPI document.

// Sample OpenAPI document with endpoints to remove
openAPIContent := `openapi: 3.1.0
info:
  title: API
  version: 1.0.0
paths:
  /users:
    get:
      summary: List users
  /users/{id}:
    get:
      summary: Get user
  /admin:
    get:
      summary: Admin endpoint
      deprecated: true`

// Overlay to remove deprecated endpoints
overlayContent := `overlay: 1.0.0
info:
  title: Cleanup Overlay
  version: 1.0.0
actions:
  - target: $.paths['/admin']
    remove: true`

// Write temporary files
openAPIFile := "temp_openapi.yaml"
overlayFile := "temp_overlay.yaml"
if err := os.WriteFile(openAPIFile, []byte(openAPIContent), 0644); err != nil {
	panic(err)
}
if err := os.WriteFile(overlayFile, []byte(overlayContent), 0644); err != nil {
	panic(err)
}
defer func() { _ = os.Remove(openAPIFile) }()
defer func() { _ = os.Remove(overlayFile) }()

overlayDoc, err := overlay.Parse(overlayFile)
if err != nil {
	panic(err)
}

openAPINode, err := loader.LoadSpecification(openAPIFile)
if err != nil {
	panic(err)
}

err = overlayDoc.ApplyTo(openAPINode)
if err != nil {
	panic(err)
}

var buf strings.Builder
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2)
err = encoder.Encode(openAPINode)
if err != nil {
	panic(err)
}

fmt.Printf("Document after removing deprecated endpoint:\n%s", buf.String())
Output:

Document after removing deprecated endpoint:
openapi: 3.1.0
info:
  title: API
  version: 1.0.0
paths:
  /users:
    get:
      summary: List users
  /users/{id}:
    get:
      summary: Get user
Example (Validating)

Example_validating demonstrates how to validate an overlay specification. Shows loading and validating an overlay specification for correctness.

package main

import (
	"fmt"
	"os"

	"github.com/speakeasy-api/openapi/overlay"
)

func main() {
	// Invalid overlay specification (missing required fields)
	invalidOverlay := `overlay: 1.0.0
info:
  title: Invalid Overlay
actions:
  - target: $.info.title
    description: Missing update or remove`

	// Write temporary file
	overlayFile := "temp_invalid_overlay.yaml"
	if err := os.WriteFile(overlayFile, []byte(invalidOverlay), 0644); err != nil {
		panic(err)
	}
	defer func() { _ = os.Remove(overlayFile) }()

	overlayDoc, err := overlay.Parse(overlayFile)
	if err != nil {
		fmt.Printf("Parse error: %s\n", err.Error())
		return
	}

	validationErr := overlayDoc.Validate()
	if validationErr != nil {
		fmt.Println("Validation errors:")
		fmt.Printf("  %s\n", validationErr.Error())
	} else {
		fmt.Println("Overlay specification is valid!")
	}
}
Output:

Validation errors:
  overlay info version must be defined

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Format

func Format(path string) error

Format will validate reformat the given file

func NewTargetSelector

func NewTargetSelector(path, method string) string

Types

type Action

type Action struct {
	Extensions `yaml:"-,inline"`

	// Target is the JSONPath to the target of the action.
	Target string `yaml:"target"`

	// Description is a description of the action.
	Description string `yaml:"description,omitempty"`

	// Update is the sub-document to use to merge or replace in the target. This is
	// ignored if Remove is set.
	Update yaml.Node `yaml:"update,omitempty"`

	// Remove marks the target node for removal rather than update.
	Remove bool `yaml:"remove,omitempty"`
}

func NewUpdateAction

func NewUpdateAction(path, method string, update yaml.Node) Action

type Extensions

type Extensions map[string]any

Extensible provides a place for extensions to be added to components of the Overlay configuration. These are a map from x-* extension fields to their values.

type Info

type Info struct {
	Extensions `yaml:"-,inline"`

	// Title is the title of the overlay.
	Title string `yaml:"title"`

	// Version is the version of the overlay.
	Version string `yaml:"version"`
}

Info describes the metadata for the overlay.

type Overlay

type Overlay struct {
	Extensions `yaml:"-,inline"`

	// Version is the version of the overlay configuration. This is only ever expected to be 1.0.0
	Version string `yaml:"overlay"`

	// JSONPathVersion should be set to rfc9535, and is used for backwards compatibility purposes
	JSONPathVersion string `yaml:"x-speakeasy-jsonpath,omitempty"`

	// Info describes the metadata for the overlay.
	Info Info `yaml:"info"`

	// Extends is a URL to the OpenAPI specification this overlay applies to.
	Extends string `yaml:"extends,omitempty"`

	// Actions is the list of actions to perform to apply the overlay.
	Actions []Action `yaml:"actions"`
}

Overlay is the top-level configuration for an OpenAPI overlay.

func Compare

func Compare(title string, y1 *yaml.Node, y2 yaml.Node) (*Overlay, error)

Compare compares input specifications from two files and returns an overlay that will convert the first into the second.

func Parse

func Parse(path string) (*Overlay, error)

Parse will parse the given reader as an overlay file.

func (*Overlay) ApplyTo

func (o *Overlay) ApplyTo(root *yaml.Node) error

ApplyTo will take an overlay and apply its changes to the given YAML document.

func (*Overlay) ApplyToStrict

func (o *Overlay) ApplyToStrict(root *yaml.Node) ([]string, error)

func (*Overlay) Format

func (o *Overlay) Format(w io.Writer) error

Format writes the file back out as YAML.

func (*Overlay) NewPath

func (o *Overlay) NewPath(target string, warnings *[]string) (Queryable, error)

func (*Overlay) ToString

func (o *Overlay) ToString() (string, error)

func (*Overlay) UsesRFC9535

func (o *Overlay) UsesRFC9535() bool

func (*Overlay) Validate

func (o *Overlay) Validate() error

type Queryable

type Queryable interface {
	Query(root *yaml.Node) []*yaml.Node
}

type ValidationErrors

type ValidationErrors []error

func (ValidationErrors) Error

func (v ValidationErrors) Error() string

func (ValidationErrors) Return

func (v ValidationErrors) Return() error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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