Skip to content

Commit

Permalink
chore: keep strict mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasRooney committed Jan 24, 2025
1 parent 3690fbf commit 552b213
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 40 deletions.
60 changes: 58 additions & 2 deletions pkg/overlay/apply.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package overlay

import (
"fmt"
"github.com/speakeasy-api/jsonpath/pkg/jsonpath"
"github.com/speakeasy-api/jsonpath/pkg/jsonpath/config"
"gopkg.in/yaml.v3"
"strings"
)

// ApplyTo will take an overlay and apply its changes to the given YAML
Expand All @@ -14,7 +16,7 @@ func (o *Overlay) ApplyTo(root *yaml.Node) error {
if action.Remove {
err = applyRemoveAction(root, action)
} else {
err = applyUpdateAction(root, action)
err = applyUpdateAction(root, action, &[]string{})
}

if err != nil {
Expand All @@ -25,6 +27,49 @@ func (o *Overlay) ApplyTo(root *yaml.Node) error {
return nil
}

func (o *Overlay) ApplyToStrict(root *yaml.Node) (error, []string) {
multiError := []string{}
warnings := []string{}
for i, action := range o.Actions {
err := validateSelectorHasAtLeastOneTarget(root, action)
if err != nil {
multiError = append(multiError, err.Error())
}
if action.Remove {
err = applyRemoveAction(root, action)
} else {
actionWarnings := []string{}
err = applyUpdateAction(root, action, &actionWarnings)
for _, warning := range actionWarnings {
warnings = append(warnings, fmt.Sprintf("update action (%v / %v) target=%s: %s", i+1, len(o.Actions), action.Target, warning))
}
}
}
if len(multiError) > 0 {
return fmt.Errorf("error applying overlay (strict): %v", strings.Join(multiError, ",")), warnings
}
return nil, warnings
}

func validateSelectorHasAtLeastOneTarget(root *yaml.Node, action Action) error {
if action.Target == "" {
return nil
}

p, err := jsonpath.NewPath(action.Target)
if err != nil {
return err
}

nodes := p.Query(root)

if len(nodes) == 0 {
return fmt.Errorf("selector %q did not match any targets", action.Target)
}

return nil
}

func applyRemoveAction(root *yaml.Node, action Action) error {
if action.Target == "" {
return nil
Expand Down Expand Up @@ -70,7 +115,7 @@ func removeNode(idx parentIndex, node *yaml.Node) {
}
}

func applyUpdateAction(root *yaml.Node, action Action) error {
func applyUpdateAction(root *yaml.Node, action Action, warnings *[]string) error {
if action.Target == "" {
return nil
}
Expand All @@ -85,12 +130,23 @@ func applyUpdateAction(root *yaml.Node, action Action) error {
}

nodes := p.Query(root)
prior, err := yaml.Marshal(root)
if err != nil {
return err
}

for _, node := range nodes {
if err := updateNode(node, action.Update); err != nil {
return err
}
}
post, err := yaml.Marshal(root)
if err != nil {
return err
}
if warnings != nil && string(prior) == string(post) {
*warnings = append(*warnings, "does nothing")
}

return nil
}
Expand Down
28 changes: 26 additions & 2 deletions pkg/overlay/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package overlay_test

import (
"bytes"
"github.com/speakeasy-api/openapi-overlay/pkg/loader"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -40,14 +41,37 @@ func NodeMatchesFile(
func TestApplyTo(t *testing.T) {
t.Parallel()

node, err := LoadSpecification("testdata/openapi.yaml")
node, err := loader.LoadSpecification("testdata/openapi.yaml")
require.NoError(t, err)

o, err := LoadOverlay("testdata/overlay.yaml")
o, err := loader.LoadOverlay("testdata/overlay.yaml")
require.NoError(t, err)

err = o.ApplyTo(node)
assert.NoError(t, err)

NodeMatchesFile(t, node, "testdata/openapi-overlayed.yaml")
}

func TestApplyToStrict(t *testing.T) {
t.Parallel()

node, err := loader.LoadSpecification("testdata/openapi.yaml")
require.NoError(t, err)

o, err := loader.LoadOverlay("testdata/overlay-mismatched.yaml")
require.NoError(t, err)

err, warnings := o.ApplyToStrict(node)
assert.Error(t, err, "error applying overlay (strict): selector \"$.unknown-attribute\" did not match any targets")
assert.Len(t, warnings, 2)
o.Actions = o.Actions[1:]
node, err = loader.LoadSpecification("testdata/openapi.yaml")
require.NoError(t, err)

err, warnings = o.ApplyToStrict(node)
assert.NoError(t, err)
assert.Len(t, warnings, 1)
assert.Equal(t, "update action (2 / 2) target=$.info.title: does nothing", warnings[0])
NodeMatchesFile(t, node, "testdata/openapi-strict-onechange.yaml")
}
33 changes: 4 additions & 29 deletions pkg/overlay/compare_test.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,23 @@
package overlay_test

import (
"fmt"
"github.com/speakeasy-api/jsonpath/pkg/overlay"
"github.com/speakeasy-api/openapi-overlay/pkg/loader"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"os"
"testing"
)

func LoadSpecification(path string) (*yaml.Node, error) {
rs, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open schema from path %q: %w", path, err)
}

var ys yaml.Node
err = yaml.NewDecoder(rs).Decode(&ys)
if err != nil {
return nil, fmt.Errorf("failed to parse schema at path %q: %w", path, err)
}

return &ys, nil
}

func LoadOverlay(path string) (*overlay.Overlay, error) {
o, err := overlay.Parse(path)
if err != nil {
return nil, fmt.Errorf("failed to parse overlay from path %q: %w", path, err)
}

return o, nil
}

func TestCompare(t *testing.T) {
t.Parallel()

node, err := LoadSpecification("testdata/openapi.yaml")
node, err := loader.LoadSpecification("testdata/openapi.yaml")
require.NoError(t, err)
node2, err := LoadSpecification("testdata/openapi-overlayed.yaml")
node2, err := loader.LoadSpecification("testdata/openapi-overlayed.yaml")
require.NoError(t, err)

o, err := LoadOverlay("testdata/overlay-generated.yaml")
o, err := loader.LoadOverlay("testdata/overlay-generated.yaml")
require.NoError(t, err)

o2, err := overlay.Compare("Drinks Overlay", node, *node2)
Expand Down
10 changes: 7 additions & 3 deletions pkg/overlay/testdata/openapi-overlayed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ paths:
type: string
responses:
"200":
description: The api key to use for authenticated endpoints.
description: ""
content:
application/json:
schema:
Expand All @@ -106,11 +106,15 @@ paths:
/drinks:
x-speakeasy-note:
"$ref": "./removeNote.yaml"
/drink/{name}: #TODO: this should be by product code and we should have search by name
/drink/{name}: # Example comment -- should be maintained
get:
operationId: getDrink
summary: Get a drink.
description: Get a drink by name, if authenticated this will include stock levels and product codes otherwise it will only include public information.
description: |
A long description
to validate that we handle indentation properly
With a second paragraph
tags:
- drinks
parameters:
Expand Down
6 changes: 3 additions & 3 deletions pkg/overlay/testdata/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ paths:
type: string
responses:
"200":
description: The api key to use for authenticated endpoints.
description: ""
content:
application/json:
schema:
Expand Down Expand Up @@ -131,7 +131,7 @@ paths:
default:
$ref: "#/components/responses/UnknownError"

/drink/{name}: #TODO: this should be by product code and we should have search by name
/drink/{name}: # Example comment -- should be maintained
get:
operationId: getDrink
summary: Get a drink.
Expand Down Expand Up @@ -452,4 +452,4 @@ components:
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
$ref: "#/components/schemas/Error"
6 changes: 6 additions & 0 deletions pkg/overlay/testdata/overlay-generated.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ actions:
"$ref": "./removeNote.yaml"
- target: $["paths"]["/drinks"]["get"]
remove: true
- target: $["paths"]["/drink/{name}"]["get"]["description"]
update: |
A long description
to validate that we handle indentation properly
With a second paragraph
- target: $["paths"]["/drink/{name}"]["get"]["parameters"]
update:
- x-parameter-extension: foo
Expand Down
2 changes: 1 addition & 1 deletion pkg/overlay/testdata/overlay-mismatched.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ info:
title: Drinks Overlay
version: 0.0.0
actions:
- target: $.unknown-attribute
- target: $["unknown-attribute"]
description: "failing overlay due to this element"
update:
description: just a description
Expand Down
7 changes: 7 additions & 0 deletions pkg/overlay/testdata/overlay.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ actions:
servers:
- url: http://localhost:35123
description: The default server.
- target: $.paths["/drink/{name}"].get
update:
description: |
A long description
to validate that we handle indentation properly
With a second paragraph
x-top-level-extension: true

0 comments on commit 552b213

Please sign in to comment.