Skip to content

About

Eliminate nesting in an OpenAPI specification in Go by promoting inline schemas, responses, request bodies, and parameters into named entries under components, replacing each with a $ref reference.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

151 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Reference Code Coverage License

A gopher flattening a stack of nested spec pages with a rolling pin, beside a tidy row of separate named cards

Give every type in your API spec a name and a home.

Code Coverage

openapi-flatten eliminates nesting in OpenAPI 3.x specifications. It promotes inline schema definitions, responses, request bodies, and parameters into the top-level components section and replaces them with $ref references.

Introduction

OpenAPI allows schemas to be defined inline anywhere they are used. While convenient for small specs, deeply nested inline definitions make large specs harder to read, harder to reuse, and harder to generate consistent client code from. Flattening gives every meaningful type a name and a single canonical location.

This matters most upstream of code generation: a generator can only emit a named, reusable Go type for a schema that has a name. Flattening first means openapi-codegen never has to invent one.

Features

  • Promotes inline schemas to components/schemas, leaving simple scalars in place
  • Promotes responses, request bodies, and parameters to their respective components sections
  • Generates readable PascalCase names with automatic collision avoidance, and, if asked, marks each with where it came from (x-flattened-from)
  • Hoists shared parameters common to every operation on a path up to the path item
  • Normalizes a common path prefix (such as /v1) into the server URLs
  • Reports errors with the full JSON path to the offending field

Usage

go get -tool github.com/MarkRosemaker/openapi-flatten/cmd/openapi-flatten

or

go get github.com/MarkRosemaker/openapi-flatten
import (
    "github.com/MarkRosemaker/openapi"
    flatten "github.com/MarkRosemaker/openapi-flatten"
)

// Load an OpenAPI document (JSON or YAML)
doc, err := openapi.LoadFromDataJSON(jsonBytes)
if err != nil {
    log.Fatal(err)
}

// Flatten all inline definitions
if err := flatten.Document(doc, flatten.Config{}); err != nil {
    log.Fatal(err)
}

// doc now has no nested inline objects — only $ref pointers

Document is the entire public API. It modifies the document in place.

With Config{MarkOrigin: true}, each schema Document moves into components/schemas gets an x-flattened-from extension: a JSON pointer to the $ref left in its place, such as #/components/schemas/Page/properties/cover. A schema without it was a component already, so a later step can tell a name the specification gave from one flatten made up. openapi-compress reads it, to prefer the specification's names when it merges schemas, and removes it. The command line tool takes -mark-origin for it.

What gets flattened

Schemas

Inline schemas are moved to components/schemas when they contain meaningful structure. Simple scalar types (integer, number, boolean, plain string) stay inline to keep the spec readable. A schema is moved when it is:

  • an object with properties
  • a string or array of strings with enum values
  • an array of objects

The entries of an allOf and the alternatives of a oneOf or anyOf are moved by the same rules, so each shaped one gets a name of its own, and so are the schema of an object's keys (propertyNames) and the schema a value must not match (not). An allOf thus ends up as a list of references, plus any entry with no shape of its own, such as {"required": ["id"]}.

Before:

{
  "paths": {
    "/pets": {
      "post": {
        "operationId": "createPet",
        "responses": {
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

After:

{
  "paths": {
    "/pets": {
      "post": {
        "operationId": "createPet",
        "responses": {
          "400": {
            "$ref": "#/components/responses/CreatePetBadRequest"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "CreatePetBadRequest": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CreatePetBadRequest"
            }
          }
        }
      }
    },
    "schemas": {
      "CreatePetBadRequest": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        }
      }
    }
  }
}

Responses

Every inline response object is moved to components/responses. The generated name combines the operation ID and the HTTP status text:

{OperationID}{StatusText}

Examples: CreatePetBadRequest, GetMeUnauthorized.

The schema of a response's content takes the same name in components/schemas, unless it has a title. The two sections keep them apart, so neither needs a suffix. Every response other than a success (2XX) always has its schema promoted to components; a success response only promotes a complex schema.

Request bodies

Inline request bodies are moved to components/requestBodies, named after the operation ID alone:

{OperationID}

Example: CreatePet. Its schema takes the same name in components/schemas, unless it has a title.

Parameters

Inline parameters are moved to components/parameters using the parameter's own name field.

Once flattened, parameters that appear on every operation of a path are hoisted up to that path item's shared parameters list, and the per-operation copies removed.

Common path prefix

If every path in the document begins with the same segment — a version prefix such as /v1, for example — that segment is stripped from the path keys and appended to each server URL instead. The effective URLs are unchanged; the paths just stop repeating themselves.

Name generation

All names are converted to Go-style PascalCase (e.g., create pet bad request → CreatePetBadRequest). If the generated name is already taken, a numeric suffix is appended (Name2, Name3, …) to avoid collisions.

An allOf entry is named by its parent, the keyword and its position (PetAllOf1). A oneOf or anyOf alternative is named by its title if it has one ("A person" → APerson), and otherwise the same way (PetOneOf1). The schema of an object's keys is named after the object plus Key (PetKey), and a not schema plus Not (PetNot).

Error reporting

Errors include the full JSON path to the offending field, powered by errpath:

paths["/pets"].post.responses["400"]["application/json"].schema: unimplemented schema ref type "null"

The openapi family

Module Purpose
openapi Parse, validate, and write OpenAPI 3.x specifications
openapi-compare Compare specification objects — exact equality and shape equivalence
openapi-edit Safe structural edits, such as renaming a schema and rewriting every $ref to it
openapi-flatten (this module) Promote inline definitions into named components entries
openapi-compress Deduplicate and merge equivalent component schemas
openapi-merge Merge schemas that were inferred independently from different samples
openapi-enrich Infer specification content from observed HTTP traffic
openapi-codegen Generate Go types, clients, and servers from a specification

Flattening naturally produces near-duplicate components, since the same shape promoted from two places gets two names. Running openapi-compress afterwards collapses them again — the two are designed to be used in that order.

Additional Information

Contributing

Contributions are welcome — please open an issue or a pull request on GitHub.

License

This project is licensed under the Apache 2.0 License.

About

Eliminate nesting in an OpenAPI specification in Go by promoting inline schemas, responses, request bodies, and parameters into named entries under components, replacing each with a $ref reference.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages