Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions cmd/openapi/commands/overlay/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import (
"os"

"github.com/speakeasy-api/openapi/cmd/openapi/commands/cmdutil"
overlayPkg "github.com/speakeasy-api/openapi/overlay"
"github.com/speakeasy-api/openapi/overlay/loader"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var (
applyOverlayFlag string
applySchemaFlag string
applyOutFlag string
applyOverlayFlags []string
applySchemaFlag string
applyOutFlag string
)

var applyCmd = &cobra.Command{
Use: "apply <overlay> [ <spec> ]",
Short: "Given an overlay, it will apply it to the spec. If omitted, spec will be loaded via extends (only from local file system).",
Use: "apply [<overlay> [<spec>]]",
Short: "Given one or more overlays, it will apply them sequentially to the spec. If omitted, spec will be loaded via extends (only from local file system).",
Args: cobra.RangeArgs(0, 2),
Run: RunApply,
Example: ` # Apply overlay using positional arguments
Expand All @@ -35,29 +36,37 @@ var applyCmd = &cobra.Command{

# Pipe spec via stdin and provide overlay as file
cat spec.yaml | openapi overlay apply overlay.yaml -
cat spec.yaml | openapi overlay apply --overlay overlay.yaml --schema -`,
cat spec.yaml | openapi overlay apply --overlay overlay.yaml --schema -

# Apply multiple overlays sequentially (repeated flag)
openapi overlay apply --overlay base.yaml --overlay env.yaml --schema spec.yaml`,
}

func init() {
applyCmd.Flags().StringVar(&applyOverlayFlag, "overlay", "", "Path to the overlay file")
applyCmd.Flags().StringSliceVar(&applyOverlayFlags, "overlay", nil, "Path to an overlay file (can be repeated or comma-separated)")
applyCmd.Flags().StringVar(&applySchemaFlag, "schema", "", "Path to the OpenAPI specification file (use '-' for stdin)")
applyCmd.Flags().StringVarP(&applyOutFlag, "out", "o", "", "Output file path (defaults to stdout)")
}

func RunApply(cmd *cobra.Command, args []string) {
// Determine overlay file path from flag or positional argument
var overlayFile string
if applyOverlayFlag != "" {
overlayFile = applyOverlayFlag
// Build the list of overlay files from flags or positional args
var overlayFiles []string
if len(applyOverlayFlags) > 0 {
overlayFiles = applyOverlayFlags
} else if len(args) > 0 {
overlayFile = args[0]
overlayFiles = []string{args[0]}
} else {
Dief("overlay file is required (use --overlay flag or provide as first argument)")
Dief("at least one overlay file is required (use --overlay flag or provide as first argument)")
}

o, err := loader.LoadOverlay(overlayFile)
if err != nil {
Die(err)
// Load all overlays upfront (fail fast on parse errors)
overlays := make([]*overlayPkg.Overlay, 0, len(overlayFiles))
for _, f := range overlayFiles {
o, err := loader.LoadOverlay(f)
if err != nil {
Dief("Failed to load overlay %q: %s", f, err.Error())
}
overlays = append(overlays, o)
}

// Determine spec file path from flag or positional argument
Expand All @@ -68,8 +77,9 @@ func RunApply(cmd *cobra.Command, args []string) {
specFile = cmdutil.ArgAt(args, 1, "")
}

// Load spec from stdin or file
// Load spec from stdin or file (use first overlay's extends as fallback)
var ys *yaml.Node
var err error
specSource := specFile
if cmdutil.IsStdin(specFile) || (specFile == "" && cmdutil.StdinIsPiped()) {
fmt.Fprintf(os.Stderr, "Reading specification from stdin\n")
Expand All @@ -79,15 +89,17 @@ func RunApply(cmd *cobra.Command, args []string) {
Die(err)
}
} else {
ys, specSource, err = loader.LoadEitherSpecification(specFile, o)
ys, specSource, err = loader.LoadEitherSpecification(specFile, overlays[0])
if err != nil {
Die(err)
}
}

err = o.ApplyTo(ys)
if err != nil {
Dief("Failed to apply overlay to spec %q: %v", specSource, err)
// Apply all overlays sequentially
for i, o := range overlays {
if err := o.ApplyTo(ys); err != nil {
Dief("Failed to apply overlay %q (#%d) to spec %q: %v", overlayFiles[i], i+1, specSource, err)
}
}

// Write to output file if specified, otherwise stdout
Expand Down