internal/openapi/service.go
Source
- Package:
openapi - File:
internal/openapi/service.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/internal/openapi/service.go
Overview
What:
Merges multiple OpenAPI fragments into a single OpenAPI document and exposes it as JSON bytes.
The merge inputs are described by a small JSON config file (default openapi-merge.config.json).
The merged output is typically persisted to dist/openapi.json and also cached in memory for reuse.
Why:
The project keeps OpenAPI definitions in fragments under specs/ and other locations to keep
the spec maintainable as the gateway grows.
This service provides a repeatable, deterministic merge step that:
- errors on duplicate paths/components (instead of silently overwriting)
- supports external references via kin-openapi's loader
- avoids repeated merge work via caching and persisted artifacts
The generated dist/openapi.json can be served by the gateway and used by tooling (clients, docs, validation).
How:
NewService(...)constructs a service with default config/dist paths (both overridable via options).(*Service).Document(ctx)is the main entrypoint:- returns an in-memory cached copy when the on-disk dist file is unchanged
- otherwise tries to read the dist file and caches it
- otherwise loads fragment files, merges them into one
openapi3.T, marshals JSON, and persists it
Merge behaviour is implemented by mergeDocuments and helpers that merge paths, components, tags, servers, and security.
Notes:
mergeDocuments mutates the first document in the slice (it becomes the merge base).
Ensure your first input represents the canonical root document (info/servers) and treat it as owned by the merge.
Contents
Imports
import block 1
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/getkin/kin-openapi/openapi3"
pkglog "github.com/theroutercompany/api_router/pkg/log"
)
Constants
const block 1
const (
defaultConfigFile = "openapi-merge.config.json"
defaultDistFile = "dist/openapi.json"
)
defaultConfigFile
What: Default filename for the OpenAPI merge configuration.
Why: Provides a convention so tooling can run without always specifying flags or env vars.
How: Used by resolveConfigPath() when OPENAPI_MERGE_CONFIG_PATH is unset.
defaultDistFile
What: Default output path for the merged OpenAPI JSON.
Why: The repo expects a stable artifact location for serving and tooling.
How: Used by NewService() (converted via filepath.FromSlash for OS portability).
Types
type block 1
type DocumentProvider interface {
Document(ctx context.Context) ([]byte, error)
}
DocumentProvider
What: Interface that exposes the merged document bytes.
Why: Lets other components depend on "document provider" without depending on the concrete service implementation.
How: Implemented by *Service via its Document method.
type block 2
type Service struct {
configPath string
distPath string
mu sync.Mutex
cache *cacheEntry
}
Service
What: Service that merges OpenAPI fragments and caches/persists the result.
Why: Provides a reusable component for both CLI tooling and runtime endpoints that need the merged OpenAPI.
How: Holds config/dist paths, a mutex, and an in-memory cache entry keyed by dist file modtime.
Notes: All public access goes through Document to ensure concurrency safety.
type block 3
type cacheEntry struct {
raw []byte
modTime time.Time
}
cacheEntry
What: In-memory cache of merged document bytes plus the dist file modtime they correspond to.
Why: Allows cachedIfCurrent to determine if the cached bytes are still valid.
How: Populated by reading the dist file or by building a new document.
Notes: The raw bytes are copied on both write and read to avoid external mutation.
type block 4
type Option func(*Service)
Option
What: Functional option for customizing a Service.
Why: Keeps the constructor stable while allowing overrides for paths and future settings.
How: Applied by NewService to mutate the constructed Service.
type block 5
type mergeConfig struct {
Inputs []mergeInput `json:"inputs"`
}
mergeConfig
What: JSON schema for the OpenAPI merge configuration file.
Why: Provides a stable format for listing fragment inputs without hardcoding paths in code.
How: Contains an inputs array of mergeInput objects.
type block 6
type mergeInput struct {
InputFile string `json:"inputFile"`
}
mergeInput
What: Single merge input entry describing one OpenAPI fragment file.
Why: Allows the config to evolve (additional fields) without changing the top-level structure.
How: The InputFile field is the path to a fragment; relative paths are resolved relative to the config file directory.
Functions and Methods
WithConfigPath
What: Option that overrides the merge configuration file path.
Why: Allows tooling and deployments to point at a different merge config without changing code.
How: When the provided path is non-empty, stores it into Service.configPath.
func WithConfigPath(path string) Option {
return func(s *Service) {
if path != "" {
s.configPath = path
}
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L47:
return func(s *Service) { if path != "" { s.configPath = path } }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L47:
func(s *Service) { if path != "" { s.configPath = path } }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L48:
if path != "" { s.configPath = path }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L49:
s.configPath = path- What: Assigns s.configPath.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L49:
- L48:
- L47:
WithDistPath
What: Option that overrides where the merged OpenAPI JSON is persisted.
Why: Some environments may want a different artifact location (e.g., writable temp dir).
How: When the provided path is non-empty, stores it into Service.distPath.
func WithDistPath(path string) Option {
return func(s *Service) {
if path != "" {
s.distPath = path
}
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L56:
return func(s *Service) { if path != "" { s.distPath = path } }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L56:
func(s *Service) { if path != "" { s.distPath = path } }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L57:
if path != "" { s.distPath = path }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L58:
s.distPath = path- What: Assigns s.distPath.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L58:
- L57:
- L56:
NewService
What: Constructs a new OpenAPI merge service.
Why: Centralizes default path selection and allows callers to override config/dist paths via functional options.
How: Sets configPath to resolveConfigPath() and distPath to the default dist file, then applies any provided options.
func NewService(opts ...Option) *Service {
s := &Service{
configPath: resolveConfigPath(),
distPath: filepath.FromSlash(defaultDistFile),
}
for _, opt := range opts {
opt(s)
}
return s
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L65:
s := &Service{ configPath: resolveConfigPath(), distPath: filepath.FromSlash(defaultDistFile), }- What: Defines s.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L70:
for _, opt := range opts { opt(s) }- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L71:
opt(s)- What: Calls opt.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L71:
- L74:
return s- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).Document
What: Returns the merged OpenAPI document as JSON bytes.
Why: Provides a single API to obtain the current merged spec for serving and tooling.
How: Under a mutex, checks whether the cached bytes are still current (based on dist file modtime), otherwise reads the dist file, otherwise rebuilds by loading and merging fragments, marshals pretty JSON, persists, caches, and returns a defensive copy.
Notes: When persisting fails, the service still returns the merged document but logs a warning and does not rely on modtime caching.
func (s *Service) Document(ctx context.Context) ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
if data, ok := s.cachedIfCurrent(); ok {
return data, nil
}
if data, modTime, err := s.readDist(); err == nil {
s.cache = &cacheEntry{raw: data, modTime: modTime}
return clone(data), nil
} else if !errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("read dist: %w", err)
}
doc, err := s.buildDocument(ctx)
if err != nil {
return nil, err
}
raw, err := json.MarshalIndent(doc, "", " ")
if err != nil {
return nil, fmt.Errorf("encode document: %w", err)
}
if err := s.persist(raw); err != nil {
pkglog.Shared().Warnw("failed to persist merged openapi document", "error", err, "path", s.distPath)
s.cache = &cacheEntry{raw: clone(raw), modTime: time.Time{}}
return clone(raw), nil
}
modTime := fileModTime(s.distPath)
s.cache = &cacheEntry{raw: clone(raw), modTime: modTime}
return clone(raw), nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L79:
s.mu.Lock()- What: Calls s.mu.Lock.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L80:
defer s.mu.Unlock()- What: Defers a call for cleanup.
- Why: Ensures the deferred action runs even on early returns.
- How: Schedules the call to run when the surrounding function returns.
- L82:
if data, ok := s.cachedIfCurrent(); ok { return data, nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L82:
data, ok := s.cachedIfCurrent()- What: Defines data, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L83:
return data, nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L82:
- L86:
if data, modTime, err := s.readDist(); err == nil { s.cache = &cacheEntry{raw: data, modTime: modTime} return clone(data), nil } else if !e…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L86:
data, modTime, err := s.readDist()- What: Defines data, modTime, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L87:
s.cache = &cacheEntry{raw: data, modTime: modTime}- What: Assigns s.cache.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L88:
return clone(data), nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L89:
if !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("read dist: %w", err) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L90:
return nil, fmt.Errorf("read dist: %w", err)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L90:
- L86:
- L93:
doc, err := s.buildDocument(ctx)- What: Defines doc, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L94:
if err != nil { return nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L95:
return nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L95:
- L98:
raw, err := json.MarshalIndent(doc, "", " ")- What: Defines raw, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L99:
if err != nil { return nil, fmt.Errorf("encode document: %w", err) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L100:
return nil, fmt.Errorf("encode document: %w", err)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L100:
- L103:
if err := s.persist(raw); err != nil { pkglog.Shared().Warnw("failed to persist merged openapi document", "error", err, "path", s.distPath)…- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L103:
err := s.persist(raw)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L104:
pkglog.Shared().Warnw("failed to persist merged openapi document", "error", err, "path", s.distPath)- What: Calls pkglog.Shared().Warnw.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L105:
s.cache = &cacheEntry{raw: clone(raw), modTime: time.Time{}}- What: Assigns s.cache.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L106:
return clone(raw), nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L103:
- L109:
modTime := fileModTime(s.distPath)- What: Defines modTime.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L110:
s.cache = &cacheEntry{raw: clone(raw), modTime: modTime}- What: Assigns s.cache.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L112:
return clone(raw), nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).cachedIfCurrent
What: Returns cached document bytes when the on-disk dist file is unchanged.
Why: Avoids re-reading/re-merging when nothing has changed and supports external regeneration of the dist file.
How: Stats the dist path and compares info.ModTime() with the cached modtime; returns a defensive copy on match.
Notes: If stat fails, this returns false and the caller falls back to reading or rebuilding.
func (s *Service) cachedIfCurrent() ([]byte, bool) {
if s.cache == nil {
return nil, false
}
info, err := os.Stat(s.distPath)
if err != nil {
return nil, false
}
if info.ModTime().Equal(s.cache.modTime) {
return clone(s.cache.raw), true
}
return nil, false
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L116:
if s.cache == nil { return nil, false }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L117:
return nil, false- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L117:
- L119:
info, err := os.Stat(s.distPath)- What: Defines info, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L120:
if err != nil { return nil, false }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L121:
return nil, false- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L121:
- L123:
if info.ModTime().Equal(s.cache.modTime) { return clone(s.cache.raw), true }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L124:
return clone(s.cache.raw), true- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L124:
- L126:
return nil, false- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).readDist
What: Reads the dist file and returns its bytes plus modification time.
Why: Allows Document() to reuse pre-generated artifacts and cache them in memory.
How: Stats the file for modtime, reads its contents, and returns both.
Notes: When the file is missing, callers treat it as a cache miss and rebuild.
func (s *Service) readDist() ([]byte, time.Time, error) {
info, err := os.Stat(s.distPath)
if err != nil {
return nil, time.Time{}, err
}
data, err := os.ReadFile(s.distPath)
if err != nil {
return nil, time.Time{}, err
}
return data, info.ModTime(), nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L130:
info, err := os.Stat(s.distPath)- What: Defines info, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L131:
if err != nil { return nil, time.Time{}, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L132:
return nil, time.Time{}, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L132:
- L135:
data, err := os.ReadFile(s.distPath)- What: Defines data, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L136:
if err != nil { return nil, time.Time{}, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L137:
return nil, time.Time{}, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L137:
- L140:
return data, info.ModTime(), nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).buildDocument
What: Loads OpenAPI fragment files and merges them into a single openapi3.T.
Why: Separates "build" logic from caching/persistence concerns.
How: Loads merge config, creates an openapi3.Loader with external refs allowed, resolves input file paths relative to the config directory, loads each fragment, and passes them to mergeDocuments.
Notes: The ctx is checked between loads so callers can cancel long merges.
func (s *Service) buildDocument(ctx context.Context) (*openapi3.T, error) {
cfg, baseDir, err := s.loadConfig()
if err != nil {
return nil, err
}
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
docs := make([]*openapi3.T, 0, len(cfg.Inputs))
for _, input := range cfg.Inputs {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
path := input.InputFile
if !filepath.IsAbs(path) {
path = filepath.Join(baseDir, path)
}
doc, err := loader.LoadFromFile(path)
if err != nil {
return nil, fmt.Errorf("load openapi fragment %s: %w", path, err)
}
docs = append(docs, doc)
}
merged, err := mergeDocuments(docs)
if err != nil {
return nil, err
}
return merged, nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L144:
cfg, baseDir, err := s.loadConfig()- What: Defines cfg, baseDir, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L145:
if err != nil { return nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L146:
return nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L146:
- L149:
loader := openapi3.NewLoader()- What: Defines loader.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L150:
loader.IsExternalRefsAllowed = true- What: Assigns loader.IsExternalRefsAllowed.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L152:
docs := make([]*openapi3.T, 0, len(cfg.Inputs))- What: Defines docs.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L153:
for _, input := range cfg.Inputs { select { case <-ctx.Done(): return nil, ctx.Err() default: } path := input.InputFile if !filepath.IsAbs(…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L154:
select { case <-ctx.Done(): return nil, ctx.Err() default: }- What: Selects among concurrent operations.
- Why: Coordinates channel operations without blocking incorrectly.
- How: Executes a
selectstatement and runs one ready case. - Nested steps:
- L155:
case <-ctx.Done():- What: Selects a select-case branch.
- Why: Coordinates concurrent operations without blocking incorrectly.
- How: Runs this case body when its channel operation is ready (or runs default immediately).
- Nested steps:
- L156:
return nil, ctx.Err()- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L156:
- L157:
default:- What: Selects a select-case branch.
- Why: Coordinates concurrent operations without blocking incorrectly.
- How: Runs this case body when its channel operation is ready (or runs default immediately).
- L155:
- L160:
path := input.InputFile- What: Defines path.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L161:
if !filepath.IsAbs(path) { path = filepath.Join(baseDir, path) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L162:
path = filepath.Join(baseDir, path)- What: Assigns path.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L162:
- L165:
doc, err := loader.LoadFromFile(path)- What: Defines doc, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L166:
if err != nil { return nil, fmt.Errorf("load openapi fragment %s: %w", path, err) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L167:
return nil, fmt.Errorf("load openapi fragment %s: %w", path, err)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L167:
- L169:
docs = append(docs, doc)- What: Assigns docs.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L154:
- L172:
merged, err := mergeDocuments(docs)- What: Defines merged, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L173:
if err != nil { return nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L174:
return nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L174:
- L177:
return merged, nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).persist
What: Persists merged JSON bytes to the configured dist path.
Why: Creates a stable artifact (dist/openapi.json) that can be served and used by tooling without re-running merges.
How: Validates that distPath is set, ensures the parent directory exists, and writes the file with standard permissions.
Notes: Failures are non-fatal to Document() (it will log and still return bytes).
func (s *Service) persist(raw []byte) error {
if s.distPath == "" {
return errors.New("dist path not configured")
}
dir := filepath.Dir(s.distPath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
if err := os.WriteFile(s.distPath, raw, 0o644); err != nil {
return err
}
return nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L181:
if s.distPath == "" { return errors.New("dist path not configured") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L182:
return errors.New("dist path not configured")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L182:
- L184:
dir := filepath.Dir(s.distPath)- What: Defines dir.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L185:
if err := os.MkdirAll(dir, 0o755); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L185:
err := os.MkdirAll(dir, 0o755)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L186:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L185:
- L188:
if err := os.WriteFile(s.distPath, raw, 0o644); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L188:
err := os.WriteFile(s.distPath, raw, 0o644)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L189:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L188:
- L191:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*Service).loadConfig
What: Reads and parses the merge configuration JSON file.
Why: The service needs an explicit input list to know which fragments to load.
How: Reads the config file, unmarshals into mergeConfig, validates that inputs are present, and returns the config plus the config directory for resolving relative paths.
Notes: Returns a user-facing error when the configuration has no inputs.
func (s *Service) loadConfig() (*mergeConfig, string, error) {
if s.configPath == "" {
return nil, "", errors.New("config path not configured")
}
raw, err := os.ReadFile(s.configPath)
if err != nil {
return nil, "", fmt.Errorf("read config: %w", err)
}
var cfg mergeConfig
if err := json.Unmarshal(raw, &cfg); err != nil {
return nil, "", fmt.Errorf("parse config: %w", err)
}
if len(cfg.Inputs) == 0 {
return nil, "", errors.New("openapi merge configuration has no inputs")
}
return &cfg, filepath.Dir(s.configPath), nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L195:
if s.configPath == "" { return nil, "", errors.New("config path not configured") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L196:
return nil, "", errors.New("config path not configured")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L196:
- L199:
raw, err := os.ReadFile(s.configPath)- What: Defines raw, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L200:
if err != nil { return nil, "", fmt.Errorf("read config: %w", err) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L201:
return nil, "", fmt.Errorf("read config: %w", err)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L201:
- L204:
var cfg mergeConfig- What: Declares local names.
- Why: Introduces variables or types used later in the function.
- How: Executes a Go declaration statement inside the function body.
- L205:
if err := json.Unmarshal(raw, &cfg); err != nil { return nil, "", fmt.Errorf("parse config: %w", err) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L205:
err := json.Unmarshal(raw, &cfg)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L206:
return nil, "", fmt.Errorf("parse config: %w", err)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L205:
- L209:
if len(cfg.Inputs) == 0 { return nil, "", errors.New("openapi merge configuration has no inputs") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L210:
return nil, "", errors.New("openapi merge configuration has no inputs")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L210:
- L213:
return &cfg, filepath.Dir(s.configPath), nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeDocuments
What: Merges multiple openapi3.T documents into a single document.
Why: Allows the OpenAPI spec to be maintained in smaller, focused fragments.
How: Uses the first document as the merge base, ensures base Paths/Components are initialized, then merges each subsequent document's paths/components and appends tags/servers/security.
Notes: The base document is mutated in place and returned.
func mergeDocuments(docs []*openapi3.T) (*openapi3.T, error) {
if len(docs) == 0 {
return nil, errors.New("no openapi documents to merge")
}
base := docs[0]
if base.Paths == nil {
base.Paths = openapi3.NewPaths()
}
if base.Components == nil {
components := openapi3.NewComponents()
base.Components = &components
}
for _, doc := range docs[1:] {
if err := mergePaths(base.Paths, doc.Paths); err != nil {
return nil, err
}
if err := mergeComponents(base.Components, doc.Components); err != nil {
return nil, err
}
base.Tags = mergeTags(base.Tags, doc.Tags)
base.Servers = mergeServers(base.Servers, doc.Servers)
base.Security = mergeSecurity(base.Security, doc.Security)
}
return base, nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L225:
if len(docs) == 0 { return nil, errors.New("no openapi documents to merge") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L226:
return nil, errors.New("no openapi documents to merge")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L226:
- L229:
base := docs[0]- What: Defines base.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L231:
if base.Paths == nil { base.Paths = openapi3.NewPaths() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L232:
base.Paths = openapi3.NewPaths()- What: Assigns base.Paths.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L232:
- L234:
if base.Components == nil { components := openapi3.NewComponents() base.Components = &components }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L235:
components := openapi3.NewComponents()- What: Defines components.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L236:
base.Components = &components- What: Assigns base.Components.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L235:
- L239:
for _, doc := range docs[1:] { if err := mergePaths(base.Paths, doc.Paths); err != nil { return nil, err } if err := mergeComponents(base.C…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L240:
if err := mergePaths(base.Paths, doc.Paths); err != nil { return nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L240:
err := mergePaths(base.Paths, doc.Paths)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L241:
return nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L240:
- L243:
if err := mergeComponents(base.Components, doc.Components); err != nil { return nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L243:
err := mergeComponents(base.Components, doc.Components)- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L244:
return nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L243:
- L246:
base.Tags = mergeTags(base.Tags, doc.Tags)- What: Assigns base.Tags.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L247:
base.Servers = mergeServers(base.Servers, doc.Servers)- What: Assigns base.Servers.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L248:
base.Security = mergeSecurity(base.Security, doc.Security)- What: Assigns base.Security.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L240:
- L251:
return base, nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergePaths
What: Merges OpenAPI path items and path extensions from one document into another.
Why: Duplicate paths are almost always accidental and should fail fast rather than silently overriding routes in the published spec.
How: Iterates source paths and inserts them into destination paths, returning an error if a path already exists; does the same for Paths.Extensions.
Notes: A nil source is treated as empty; a nil destination is treated as a programming error.
func mergePaths(dst, src *openapi3.Paths) error {
if src == nil {
return nil
}
if dst == nil {
return errors.New("destination paths not initialised")
}
dstMap := dst.Map()
for path, item := range src.Map() {
if _, exists := dstMap[path]; exists {
return fmt.Errorf("duplicate path detected: %s", path)
}
dst.Set(path, item)
}
if len(src.Extensions) > 0 {
if dst.Extensions == nil {
dst.Extensions = make(map[string]interface{}, len(src.Extensions))
}
for key, value := range src.Extensions {
if _, exists := dst.Extensions[key]; exists {
return fmt.Errorf("duplicate path extension detected: %s", key)
}
dst.Extensions[key] = value
}
}
return nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L255:
if src == nil { return nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L256:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L256:
- L258:
if dst == nil { return errors.New("destination paths not initialised") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L259:
return errors.New("destination paths not initialised")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L259:
- L262:
dstMap := dst.Map()- What: Defines dstMap.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L263:
for path, item := range src.Map() { if _, exists := dstMap[path]; exists { return fmt.Errorf("duplicate path detected: %s", path) } dst.Set…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L264:
if _, exists := dstMap[path]; exists { return fmt.Errorf("duplicate path detected: %s", path) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L264:
_, exists := dstMap[path]- What: Defines _, exists.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L265:
return fmt.Errorf("duplicate path detected: %s", path)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L264:
- L267:
dst.Set(path, item)- What: Calls dst.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L264:
- L270:
if len(src.Extensions) > 0 { if dst.Extensions == nil { dst.Extensions = make(map[string]interface{}, len(src.Extensions)) } for key, value…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L271:
if dst.Extensions == nil { dst.Extensions = make(map[string]interface{}, len(src.Extensions)) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L272:
dst.Extensions = make(map[string]interface{}, len(src.Extensions))- What: Assigns dst.Extensions.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L272:
- L274:
for key, value := range src.Extensions { if _, exists := dst.Extensions[key]; exists { return fmt.Errorf("duplicate path extension detected…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L275:
if _, exists := dst.Extensions[key]; exists { return fmt.Errorf("duplicate path extension detected: %s", key) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L275:
_, exists := dst.Extensions[key]- What: Defines _, exists.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L276:
return fmt.Errorf("duplicate path extension detected: %s", key)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L275:
- L278:
dst.Extensions[key] = value- What: Assigns dst.Extensions[key].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L275:
- L271:
- L282:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeComponents
What: Merges OpenAPI Components blocks (schemas, responses, security schemes, and more).
Why: Component definitions must be unique across fragments to avoid ambiguous references.
How: Calls mergeComponentMap for each supported component category and returns an error on the first duplicate.
Notes: A nil source is treated as empty; a nil destination is treated as a programming error.
func mergeComponents(dst, src *openapi3.Components) error {
if src == nil {
return nil
}
if dst == nil {
return errors.New("destination components not initialised")
}
if err := mergeComponentMap(&dst.Schemas, src.Schemas, "schema"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Parameters, src.Parameters, "parameter"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Headers, src.Headers, "header"); err != nil {
return err
}
if err := mergeComponentMap(&dst.RequestBodies, src.RequestBodies, "request body"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Responses, src.Responses, "response"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Examples, src.Examples, "example"); err != nil {
return err
}
if err := mergeComponentMap(&dst.SecuritySchemes, src.SecuritySchemes, "security scheme"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Links, src.Links, "link"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Callbacks, src.Callbacks, "callback"); err != nil {
return err
}
if err := mergeComponentMap(&dst.Extensions, src.Extensions, "extension"); err != nil {
return err
}
return nil
}
Walkthrough
Expand walkthrough (35 steps)
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L286:
if src == nil { return nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L287:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L287:
- L289:
if dst == nil { return errors.New("destination components not initialised") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L290:
return errors.New("destination components not initialised")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L290:
- L293:
if err := mergeComponentMap(&dst.Schemas, src.Schemas, "schema"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L293:
err := mergeComponentMap(&dst.Schemas, src.Schemas, "schema")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L294:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L293:
- L296:
if err := mergeComponentMap(&dst.Parameters, src.Parameters, "parameter"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L296:
err := mergeComponentMap(&dst.Parameters, src.Parameters, "parameter")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L297:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L296:
- L299:
if err := mergeComponentMap(&dst.Headers, src.Headers, "header"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L299:
err := mergeComponentMap(&dst.Headers, src.Headers, "header")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L300:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L299:
- L302:
if err := mergeComponentMap(&dst.RequestBodies, src.RequestBodies, "request body"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L302:
err := mergeComponentMap(&dst.RequestBodies, src.RequestBodies, "request body")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L303:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L302:
- L305:
if err := mergeComponentMap(&dst.Responses, src.Responses, "response"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L305:
err := mergeComponentMap(&dst.Responses, src.Responses, "response")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L306:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L305:
- L308:
if err := mergeComponentMap(&dst.Examples, src.Examples, "example"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L308:
err := mergeComponentMap(&dst.Examples, src.Examples, "example")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L309:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L308:
- L311:
if err := mergeComponentMap(&dst.SecuritySchemes, src.SecuritySchemes, "security scheme"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L311:
err := mergeComponentMap(&dst.SecuritySchemes, src.SecuritySchemes, "security scheme")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L312:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L311:
- L314:
if err := mergeComponentMap(&dst.Links, src.Links, "link"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L314:
err := mergeComponentMap(&dst.Links, src.Links, "link")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L315:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L314:
- L317:
if err := mergeComponentMap(&dst.Callbacks, src.Callbacks, "callback"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L317:
err := mergeComponentMap(&dst.Callbacks, src.Callbacks, "callback")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L318:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L317:
- L320:
if err := mergeComponentMap(&dst.Extensions, src.Extensions, "extension"); err != nil { return err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L320:
err := mergeComponentMap(&dst.Extensions, src.Extensions, "extension")- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L321:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L320:
- L324:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeComponentMap
What: Generic helper to merge OpenAPI component maps while rejecting duplicates.
Why: Component maps share the same semantics across categories (schemas, responses, headers, etc).
How: Ensures the destination map is initialized, then copies entries from src into dst, returning an error when a key already exists.
Notes: The label string is used only to make duplicate errors easier to interpret.
func mergeComponentMap[M ~map[string]V, V any](dst *M, src M, label string) error {
if len(src) == 0 {
return nil
}
if *dst == nil {
*dst = make(M, len(src))
}
for key, value := range src {
if _, exists := (*dst)[key]; exists {
return fmt.Errorf("duplicate %s detected: %s", label, key)
}
(*dst)[key] = value
}
return nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L328:
if len(src) == 0 { return nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L329:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L329:
- L331:
if *dst == nil { *dst = make(M, len(src)) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L332:
*dst = make(M, len(src))- What: Assigns *dst.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L332:
- L334:
for key, value := range src { if _, exists := (*dst)[key]; exists { return fmt.Errorf("duplicate %s detected: %s", label, key) } (*dst)[key…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L335:
if _, exists := (*dst)[key]; exists { return fmt.Errorf("duplicate %s detected: %s", label, key) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L335:
_, exists := (*dst)[key]- What: Defines _, exists.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L336:
return fmt.Errorf("duplicate %s detected: %s", label, key)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L335:
- L338:
(*dst)[key] = value- What: Assigns (*dst)[key].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L335:
- L340:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeTags
What: Merges OpenAPI tag entries while avoiding duplicates.
Why: Tags are used for grouping and documentation; duplicates reduce readability.
How: Builds a set of existing tag names and appends only new names from the source list.
Notes: Nil tag entries are ignored.
func mergeTags(dst, src openapi3.Tags) openapi3.Tags {
if len(src) == 0 {
return dst
}
existing := make(map[string]struct{}, len(dst))
for _, tag := range dst {
if tag != nil {
existing[tag.Name] = struct{}{}
}
}
for _, tag := range src {
if tag == nil {
continue
}
if _, ok := existing[tag.Name]; ok {
continue
}
dst = append(dst, tag)
existing[tag.Name] = struct{}{}
}
return dst
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L344:
if len(src) == 0 { return dst }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L345:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L345:
- L348:
existing := make(map[string]struct{}, len(dst))- What: Defines existing.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L349:
for _, tag := range dst { if tag != nil { existing[tag.Name] = struct{}{} } }- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L350:
if tag != nil { existing[tag.Name] = struct{}{} }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L351:
existing[tag.Name] = struct{}{}- What: Assigns existing[tag.Name].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L351:
- L350:
- L355:
for _, tag := range src { if tag == nil { continue } if _, ok := existing[tag.Name]; ok { continue } dst = append(dst, tag) existing[tag.Na…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L356:
if tag == nil { continue }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L357:
continue- What: Executes a statement.
- Why: Advances the function logic.
- How: Runs this statement as part of the function body.
- L357:
- L359:
if _, ok := existing[tag.Name]; ok { continue }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L359:
_, ok := existing[tag.Name]- What: Defines _, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L360:
continue- What: Executes a statement.
- Why: Advances the function logic.
- How: Runs this statement as part of the function body.
- L359:
- L362:
dst = append(dst, tag)- What: Assigns dst.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L363:
existing[tag.Name] = struct{}{}- What: Assigns existing[tag.Name].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L356:
- L365:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeServers
What: Merges OpenAPI server entries while avoiding duplicates.
Why: Servers may be defined in multiple fragments; duplicates are noise and can confuse readers.
How: Builds a set of existing server URLs and appends only new URLs from the source list.
Notes: Nil server entries are ignored.
func mergeServers(dst, src openapi3.Servers) openapi3.Servers {
if len(src) == 0 {
return dst
}
existing := make(map[string]struct{}, len(dst))
for _, server := range dst {
if server != nil {
existing[server.URL] = struct{}{}
}
}
for _, server := range src {
if server == nil {
continue
}
if _, ok := existing[server.URL]; ok {
continue
}
dst = append(dst, server)
existing[server.URL] = struct{}{}
}
return dst
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L369:
if len(src) == 0 { return dst }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L370:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L370:
- L373:
existing := make(map[string]struct{}, len(dst))- What: Defines existing.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L374:
for _, server := range dst { if server != nil { existing[server.URL] = struct{}{} } }- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L375:
if server != nil { existing[server.URL] = struct{}{} }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L376:
existing[server.URL] = struct{}{}- What: Assigns existing[server.URL].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L376:
- L375:
- L380:
for _, server := range src { if server == nil { continue } if _, ok := existing[server.URL]; ok { continue } dst = append(dst, server) exis…- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L381:
if server == nil { continue }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L382:
continue- What: Executes a statement.
- Why: Advances the function logic.
- How: Runs this statement as part of the function body.
- L382:
- L384:
if _, ok := existing[server.URL]; ok { continue }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L384:
_, ok := existing[server.URL]- What: Defines _, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L385:
continue- What: Executes a statement.
- Why: Advances the function logic.
- How: Runs this statement as part of the function body.
- L384:
- L387:
dst = append(dst, server)- What: Assigns dst.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L388:
existing[server.URL] = struct{}{}- What: Assigns existing[server.URL].
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L381:
- L390:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
mergeSecurity
What: Merges OpenAPI security requirements slices.
Why: Security requirements are additive across fragments.
How: Appends the source requirements to the destination slice when source is non-empty.
Notes: This helper does not deduplicate requirements.
func mergeSecurity(dst, src openapi3.SecurityRequirements) openapi3.SecurityRequirements {
if len(src) == 0 {
return dst
}
return append(dst, src...)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L394:
if len(src) == 0 { return dst }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L395:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L395:
- L397:
return append(dst, src...)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
resolveConfigPath
What: Resolves the merge configuration path from environment or default.
Why: Lets operators override config location without rebuilding the binary.
How: Returns OPENAPI_MERGE_CONFIG_PATH when set; otherwise returns the default filename converted with filepath.FromSlash.
func resolveConfigPath() string {
if path := os.Getenv("OPENAPI_MERGE_CONFIG_PATH"); path != "" {
return path
}
return filepath.FromSlash(defaultConfigFile)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L401:
if path := os.Getenv("OPENAPI_MERGE_CONFIG_PATH"); path != "" { return path }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L401:
path := os.Getenv("OPENAPI_MERGE_CONFIG_PATH")- What: Defines path.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L402:
return path- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L401:
- L404:
return filepath.FromSlash(defaultConfigFile)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
fileModTime
What: Returns a file's modification time (or zero).
Why: Used to associate cached bytes with the on-disk dist file for freshness checks.
How: Stats the path and returns info.ModTime(); returns time.Time{} on stat failure.
func fileModTime(path string) time.Time {
info, err := os.Stat(path)
if err != nil {
return time.Time{}
}
return info.ModTime()
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L408:
info, err := os.Stat(path)- What: Defines info, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L409:
if err != nil { return time.Time{} }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L410:
return time.Time{}- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L410:
- L412:
return info.ModTime()- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
clone
What: Creates a defensive copy of a byte slice.
Why: Prevents callers from mutating cached document bytes returned by Document().
How: Allocates a new slice of the same length and copies the contents.
Notes: Returns nil for a nil input slice.
func clone(src []byte) []byte {
if src == nil {
return nil
}
dst := make([]byte, len(src))
copy(dst, src)
return dst
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L416:
if src == nil { return nil }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L417:
return nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L417:
- L419:
dst := make([]byte, len(src))- What: Defines dst.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L420:
copy(dst, src)- What: Calls copy.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L421:
return dst- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).