Skip to content
Merged
Show file tree
Hide file tree
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
67 changes: 52 additions & 15 deletions cmd/openapi/commands/overlay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ OpenAPI Overlays provide a way to modify OpenAPI and Arazzo specifications witho
Apply an overlay to an OpenAPI specification.

```bash
# Apply overlay to a specification
# Apply overlay to a specification (positional arguments)
openapi overlay apply overlay.yaml spec.yaml

# Apply overlay to a specification (flags)
openapi overlay apply --overlay overlay.yaml --schema spec.yaml

# Apply overlay with output to file
openapi overlay apply --overlay overlay.yaml --schema spec.yaml --out modified-spec.yaml

# Apply overlay when overlay has extends key set
openapi overlay apply overlay.yaml
# or
openapi overlay apply --overlay overlay.yaml
```

Expand All @@ -46,17 +51,21 @@ Features:
- Supports all OpenAPI Overlay Specification operations
- Handles complex nested modifications
- Preserves original document structure where not modified
- Supports both positional arguments and explicit flags

### `validate`

Validate an overlay file for compliance with the OpenAPI Overlay Specification.

```bash
# Validate an overlay file
# Validate an overlay file (positional argument)
openapi overlay validate overlay.yaml

# Validate an overlay file (flag)
openapi overlay validate --overlay overlay.yaml

# Validate with verbose output
openapi overlay validate -v --overlay overlay.yaml
openapi overlay validate -v overlay.yaml
```

This command checks for:
Expand All @@ -73,11 +82,14 @@ Note: This validates the overlay file structure itself, not whether it will appl
Generate an OpenAPI Overlay specification from two input files.

```bash
# Generate overlay from two specifications
openapi overlay compare --before spec1.yaml --after spec2.yaml --out overlay.yaml
# Generate overlay from two specifications (positional arguments)
openapi overlay compare spec1.yaml spec2.yaml

# Generate overlay with console output
# Generate overlay from two specifications (flags)
openapi overlay compare --before spec1.yaml --after spec2.yaml

# Generate overlay with output to file
openapi overlay compare --before spec1.yaml --after spec2.yaml --out overlay.yaml
```

Features:
Expand All @@ -86,6 +98,7 @@ Features:
- Generates overlay operations for all changes
- Provides diagnostic output showing detected changes
- Creates overlay files that can recreate the transformation
- Supports both positional arguments and explicit flags

### `upgrade`

Expand Down Expand Up @@ -172,9 +185,24 @@ All commands support these common options:

- `-h, --help`: Show help for the command
- `-v, --verbose`: Enable verbose output (global flag)
- `--overlay`: Path to the overlay file
- `--schema`: Path to the OpenAPI specification (for apply command)
- `--out`: Output file path (optional, defaults to stdout)

Command-specific flags:

**apply command:**

- `--overlay`: Path to the overlay file (alternative to positional argument)
- `--schema`: Path to the OpenAPI specification (alternative to positional argument)
- `-o, --out`: Output file path (optional, defaults to stdout)

**validate command:**

- `--overlay`: Path to the overlay file (alternative to positional argument)

**compare command:**

- `--before`: Path to the first (before) specification file (alternative to positional argument)
- `--after`: Path to the second (after) specification file (alternative to positional argument)
- `-o, --out`: Output file path (optional, defaults to stdout)

## Output Formats

Expand All @@ -185,30 +213,39 @@ All commands work with both YAML and JSON input files, but always output YAML at
### Basic Workflow

```bash
# Create an overlay by comparing two specs
# Create an overlay by comparing two specs (using flags)
openapi overlay compare --before original.yaml --after modified.yaml --out changes.overlay.yaml

# Or using positional arguments
openapi overlay compare original.yaml modified.yaml --out changes.overlay.yaml

# Validate the generated overlay
openapi overlay validate --overlay changes.overlay.yaml
openapi overlay validate changes.overlay.yaml

# Apply the overlay to the original spec
# Apply the overlay to the original spec (using flags)
openapi overlay apply --overlay changes.overlay.yaml --schema original.yaml --out final.yaml

# Or using positional arguments
openapi overlay apply changes.overlay.yaml original.yaml --out final.yaml
```

### Environment-Specific Modifications

```bash
# Apply production overlay
# Apply production overlay (using flags)
openapi overlay apply --overlay prod.overlay.yaml --schema base-spec.yaml --out prod-spec.yaml

# Or using positional arguments
openapi overlay apply prod.overlay.yaml base-spec.yaml --out prod-spec.yaml

# Apply development overlay
openapi overlay apply --overlay dev.overlay.yaml --schema base-spec.yaml --out dev-spec.yaml
openapi overlay apply dev.overlay.yaml base-spec.yaml --out dev-spec.yaml
```

### Integration with Other Commands

```bash
# Validate base spec, apply overlay, then validate result
openapi spec validate ./base-spec.yaml
openapi overlay apply --overlay ./modifications.yaml --schema ./base-spec.yaml --out ./modified-spec.yaml
openapi overlay apply ./modifications.yaml ./base-spec.yaml --out ./modified-spec.yaml
openapi spec validate ./modified-spec.yaml
54 changes: 50 additions & 4 deletions cmd/openapi/commands/overlay/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,60 @@ import (
"gopkg.in/yaml.v3"
)

var (
applyOverlayFlag 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).",
Args: cobra.RangeArgs(1, 2),
Args: cobra.RangeArgs(0, 2),
Run: RunApply,
Example: ` # Apply overlay using positional arguments
openapi overlay apply overlay.yaml spec.yaml

# Apply overlay using flags
openapi overlay apply --overlay overlay.yaml --schema spec.yaml

# Apply overlay with output to file
openapi overlay apply --overlay overlay.yaml --schema spec.yaml --out modified-spec.yaml

# Apply overlay when overlay has extends key set
openapi overlay apply overlay.yaml`,
}

func init() {
applyCmd.Flags().StringVar(&applyOverlayFlag, "overlay", "", "Path to the overlay file")
applyCmd.Flags().StringVar(&applySchemaFlag, "schema", "", "Path to the OpenAPI specification file")
applyCmd.Flags().StringVarP(&applyOutFlag, "out", "o", "", "Output file path (defaults to stdout)")
}

func RunApply(cmd *cobra.Command, args []string) {
overlayFile := args[0]
// Determine overlay file path from flag or positional argument
var overlayFile string
if applyOverlayFlag != "" {
overlayFile = applyOverlayFlag
} else if len(args) > 0 {
overlayFile = args[0]
} else {
Dief("overlay file is required (use --overlay flag or provide as first argument)")
}

o, err := loader.LoadOverlay(overlayFile)
if err != nil {
Die(err)
}

// Determine spec file path from flag or positional argument
var specFile string
if len(args) > 1 {
if applySchemaFlag != "" {
specFile = applySchemaFlag
} else if len(args) > 1 {
specFile = args[1]
}

ys, specFile, err := loader.LoadEitherSpecification(specFile, o)
if err != nil {
Die(err)
Expand All @@ -37,7 +72,18 @@ func RunApply(cmd *cobra.Command, args []string) {
Dief("Failed to apply overlay to spec file %q: %v", specFile, err)
}

err = yaml.NewEncoder(os.Stdout).Encode(ys)
// Write to output file if specified, otherwise stdout
out := os.Stdout
if applyOutFlag != "" {
f, err := os.Create(applyOutFlag)
if err != nil {
Dief("Failed to create output file %q: %v", applyOutFlag, err)
}
defer f.Close()
out = f
}

err = yaml.NewEncoder(out).Encode(ys)
if err != nil {
Dief("Failed to encode spec file %q: %v", specFile, err)
}
Expand Down
67 changes: 59 additions & 8 deletions cmd/openapi/commands/overlay/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,83 @@ import (
"github.com/spf13/cobra"
)

var (
compareBeforeFlag string
compareAfterFlag string
compareOutFlag string
)

var compareCmd = &cobra.Command{
Use: "compare <spec1> <spec2>",
Short: "Given two specs, it will output an overlay that describes the differences between them",
Args: cobra.ExactArgs(2),
Args: cobra.RangeArgs(0, 2),
Run: RunCompare,
Example: ` # Compare specs using positional arguments
openapi overlay compare spec1.yaml spec2.yaml

# Compare specs using flags
openapi overlay compare --before spec1.yaml --after spec2.yaml

# Compare specs with output to file
openapi overlay compare --before spec1.yaml --after spec2.yaml --out overlay.yaml`,
}

func init() {
compareCmd.Flags().StringVar(&compareBeforeFlag, "before", "", "Path to the first (before) specification file")
compareCmd.Flags().StringVar(&compareAfterFlag, "after", "", "Path to the second (after) specification file")
compareCmd.Flags().StringVarP(&compareOutFlag, "out", "o", "", "Output file path (defaults to stdout)")
}

func RunCompare(cmd *cobra.Command, args []string) {
y1, err := loader.LoadSpecification(args[0])
// Determine first spec file path from flag or positional argument
var spec1 string
if compareBeforeFlag != "" {
spec1 = compareBeforeFlag
} else if len(args) > 0 {
spec1 = args[0]
} else {
Dief("first specification file is required (use --before flag or provide as first argument)")
}

// Determine second spec file path from flag or positional argument
var spec2 string
if compareAfterFlag != "" {
spec2 = compareAfterFlag
} else if len(args) > 1 {
spec2 = args[1]
} else {
Dief("second specification file is required (use --after flag or provide as second argument)")
}

y1, err := loader.LoadSpecification(spec1)
if err != nil {
Dief("Failed to load %q: %v", args[0], err)
Dief("Failed to load %q: %v", spec1, err)
}

y2, err := loader.LoadSpecification(args[1])
y2, err := loader.LoadSpecification(spec2)
if err != nil {
Dief("Failed to load %q: %v", args[1], err)
Dief("Failed to load %q: %v", spec2, err)
}

title := fmt.Sprintf("Overlay %s => %s", args[0], args[1])
title := fmt.Sprintf("Overlay %s => %s", spec1, spec2)

o, err := overlay.Compare(title, y1, *y2)
if err != nil {
Dief("Failed to compare spec files %q and %q: %v", args[0], args[1], err)
Dief("Failed to compare spec files %q and %q: %v", spec1, spec2, err)
}

// Write to output file if specified, otherwise stdout
out := os.Stdout
if compareOutFlag != "" {
f, err := os.Create(compareOutFlag)
if err != nil {
Dief("Failed to create output file %q: %v", compareOutFlag, err)
}
defer f.Close()
out = f
}

err = o.Format(os.Stdout)
err = o.Format(out)
if err != nil {
Dief("Failed to format overlay: %v", err)
}
Expand Down
29 changes: 25 additions & 4 deletions cmd/openapi/commands/overlay/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,44 @@ import (
"github.com/spf13/cobra"
)

var validateOverlayFlag string

var validateCmd = &cobra.Command{
Use: "validate <overlay>",
Short: "Given an overlay, it will state whether it appears to be valid or describe the problems found",
Args: cobra.ExactArgs(1),
Args: cobra.RangeArgs(0, 1),
Run: RunValidateOverlay,
Example: ` # Validate overlay using positional argument
openapi overlay validate overlay.yaml

# Validate overlay using flag
openapi overlay validate --overlay overlay.yaml`,
}

func init() {
validateCmd.Flags().StringVar(&validateOverlayFlag, "overlay", "", "Path to the overlay file")
}

func RunValidateOverlay(cmd *cobra.Command, args []string) {
o, err := loader.LoadOverlay(args[0])
// Determine overlay file path from flag or positional argument
var overlayFile string
if validateOverlayFlag != "" {
overlayFile = validateOverlayFlag
} else if len(args) > 0 {
overlayFile = args[0]
} else {
Dief("overlay file is required (use --overlay flag or provide as first argument)")
}

o, err := loader.LoadOverlay(overlayFile)
if err != nil {
Die(err)
}

err = o.Validate()
if err != nil {
Dief("Overlay file %q failed validation:\n%v", args[0], err)
Dief("Overlay file %q failed validation:\n%v", overlayFile, err)
}

fmt.Printf("Overlay file %q is valid.\n", args[0])
fmt.Printf("Overlay file %q is valid.\n", overlayFile)
}
13 changes: 13 additions & 0 deletions mise-tasks/test-cli
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ $CLI overlay compare overlay/testdata/openapi.yaml overlay/testdata/openapi-over
echo " ✓ Validating generated overlay..."
$CLI overlay validate dist/test/test-overlay-generated.yaml > /dev/null

# Test overlay commands with flags
echo " ✓ Testing overlay validate with --overlay flag..."
$CLI overlay validate --overlay overlay/testdata/overlay.yaml > /dev/null

echo " ✓ Testing overlay apply with --overlay and --schema flags..."
$CLI overlay apply --overlay overlay/testdata/overlay.yaml --schema overlay/testdata/openapi.yaml > dist/test/test-overlayed-flags.yaml

echo " ✓ Testing overlay compare with --before and --after flags..."
$CLI overlay compare --before overlay/testdata/openapi.yaml --after overlay/testdata/openapi-overlayed.yaml > dist/test/test-overlay-generated-flags.yaml

echo " ✓ Validating overlay generated with flags..."
$CLI overlay validate --overlay dist/test/test-overlay-generated-flags.yaml > /dev/null

# Test error cases - commands that should fail
echo " ✓ Testing error cases..."

Expand Down
Loading