diff --git a/cmd/wasm/functions.go b/cmd/wasm/functions.go index c165f45..bbd4ffe 100644 --- a/cmd/wasm/functions.go +++ b/cmd/wasm/functions.go @@ -10,7 +10,6 @@ import ( "github.com/speakeasy-api/jsonpath/pkg/jsonpath" "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config" - "github.com/speakeasy-api/jsonpath/pkg/jsonpath/token" "github.com/speakeasy-api/openapi/overlay" "gopkg.in/yaml.v3" ) @@ -34,7 +33,6 @@ func CalculateOverlay(originalYAML, targetYAML, existingOverlay string) (string, if err != nil { return "", fmt.Errorf("failed to parse overlay schema in CalculateOverlay: %w", err) } - existingOverlayDocument.JSONPathVersion = "rfc9535" // force this in the playground. // now modify the original using the existing overlay err = existingOverlayDocument.ApplyTo(&orig) if err != nil { @@ -46,8 +44,16 @@ func CalculateOverlay(originalYAML, targetYAML, existingOverlay string) (string, return "", fmt.Errorf("failed to compare schemas: %w", err) } // special case, is there only one action and it targets the same as the last overlayDocument.Actions item entry, we'll just replace it. - if len(newOverlay.Actions) == 1 && len(existingOverlayDocument.Actions) > 0 && newOverlay.Actions[0].Target == existingOverlayDocument.Actions[len(existingOverlayDocument.Actions)-1].Target { - existingOverlayDocument.Actions[len(existingOverlayDocument.Actions)-1] = newOverlay.Actions[0] + lastAction := len(existingOverlayDocument.Actions) - 1 + if len(newOverlay.Actions) == 1 && lastAction >= 0 && newOverlay.Actions[0].Target == existingOverlayDocument.Actions[lastAction].Target { + prev := &existingOverlayDocument.Actions[lastAction] + next := &newOverlay.Actions[0] + // If both are sequence updates, concatenate content rather than replacing + if prev.Update.Kind == yaml.SequenceNode && next.Update.Kind == yaml.SequenceNode { + prev.Update.Content = append(prev.Update.Content, next.Update.Content...) + } else { + existingOverlayDocument.Actions[lastAction] = *next + } } else { // Otherwise, we'll just append the new overlay to the existing overlay existingOverlayDocument.Actions = append(existingOverlayDocument.Actions, newOverlay.Actions...) @@ -115,16 +121,8 @@ func ApplyOverlay(originalYAML, overlayYAML string) (string, error) { if err != nil { return "", fmt.Errorf("failed to validate overlay schema in ApplyOverlay: %w", err) } - hasFilterExpression := false - // check to see if we have an overlay with an error, or a partial overlay: i.e. any overlay actions are missing an update or remove + // If an action has a valid target but no operation (update, remove, or copy), return query results for the target path (explorer mode). for i, action := range overlay.Actions { - tokenized := token.NewTokenizer(action.Target, config.WithPropertyNameExtension()).Tokenize() - for _, tok := range tokenized { - if tok.Token == token.FILTER { - hasFilterExpression = true - break - } - } parsed, pathErr := jsonpath.NewPath(action.Target, config.WithPropertyNameExtension()) var node *yaml.Node @@ -136,7 +134,7 @@ func ApplyOverlay(originalYAML, overlayYAML string) (string, error) { return applyOverlayJSONPathError(pathErr, node) } - if reflect.ValueOf(action.Update).IsZero() && action.Remove == false { + if reflect.ValueOf(action.Update).IsZero() && !action.Remove && action.Copy == "" { result := parsed.Query(&orig) node, err = lookupOverlayActionTargetNode(overlayYAML, i) @@ -147,10 +145,6 @@ func ApplyOverlay(originalYAML, overlayYAML string) (string, error) { return applyOverlayJSONPathIncomplete(result, node) } } - if hasFilterExpression && overlay.JSONPathVersion != "rfc9535" { - return "", fmt.Errorf("invalid overlay schema: must have `x-speakeasy-jsonpath: rfc9535`") - } - err = overlay.ApplyTo(&orig) if err != nil { return "", fmt.Errorf("failed to apply overlay: %w", err) diff --git a/go.mod b/go.mod index 662f435..43815de 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/speakeasy-api/openapi v1.16.2 + github.com/speakeasy-api/openapi v1.18.1 github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 5f39949..98c865c 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/speakeasy-api/openapi v1.16.2 h1:ntN57Z4isk3jfckYf7L957/2JtZazpfm8qg76pPlpR0= -github.com/speakeasy-api/openapi v1.16.2/go.mod h1:aiVj+JnirrwZDtKegt0hQrj/ixl3v17EkN2YGnTuSro= +github.com/speakeasy-api/openapi v1.18.1 h1:re8tzO8u5U8q33z9mm1UiuiGo9fLrHJgk238WKYdIZM= +github.com/speakeasy-api/openapi v1.18.1/go.mod h1:aiVj+JnirrwZDtKegt0hQrj/ixl3v17EkN2YGnTuSro= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= diff --git a/pkg/jsonpath/jsonpath.go b/pkg/jsonpath/jsonpath.go index d5c8a5b..82c5122 100644 --- a/pkg/jsonpath/jsonpath.go +++ b/pkg/jsonpath/jsonpath.go @@ -12,7 +12,7 @@ func NewPath(input string, opts ...config.Option) (*JSONPath, error) { tokens := tokenizer.Tokenize() for i := 0; i < len(tokens); i++ { if tokens[i].Token == token.ILLEGAL { - return nil, fmt.Errorf(tokenizer.ErrorString(&tokens[i], "unexpected token")) + return nil, fmt.Errorf("%s", tokenizer.ErrorString(&tokens[i], "unexpected token")) } } parser := newParserPrivate(tokenizer, tokens, opts...) diff --git a/pkg/jsonpath/token/token_test.go b/pkg/jsonpath/token/token_test.go index d8c7931..c5e5a16 100644 --- a/pkg/jsonpath/token/token_test.go +++ b/pkg/jsonpath/token/token_test.go @@ -610,12 +610,12 @@ func TestTokenizer_categorize(t *testing.T) { if token.Token == ILLEGAL { foundIllegal = true if !tc.illegal { - t.Errorf(tokenizer.ErrorString(&token, "Illegal Token")) + t.Errorf("%s", tokenizer.ErrorString(&token, "Illegal Token")) } } } if tc.illegal && !foundIllegal { - t.Errorf(tokenizer.ErrorTokenString(&tokenizedJsonPath[0], "Expected an illegal token")) + t.Errorf("%s", tokenizer.ErrorTokenString(&tokenizedJsonPath[0], "Expected an illegal token")) } if tc.simple && foundIllegal { @@ -632,12 +632,12 @@ func TestTokenizer_categorize(t *testing.T) { } } if !simple { - t.Errorf(tokenizer.ErrorString(&token, "Expected a simple path, but found a non-simple token")) + t.Errorf("%s", tokenizer.ErrorString(&token, "Expected a simple path, but found a non-simple token")) } } } if !tc.simple && tokenizedJsonPath.IsSimple() { - t.Errorf(tokenizer.ErrorTokenString(&tokenizedJsonPath[0], "Expected a non-simple path, but found it was simple")) + t.Errorf("%s", tokenizer.ErrorTokenString(&tokenizedJsonPath[0], "Expected a non-simple path, but found it was simple")) } }) } diff --git a/web/src/assets/wasm/lib.wasm b/web/src/assets/wasm/lib.wasm index ea407a5..a5e02ea 100755 Binary files a/web/src/assets/wasm/lib.wasm and b/web/src/assets/wasm/lib.wasm differ diff --git a/web/src/defaults.ts b/web/src/defaults.ts index 45a7445..0d7d78c 100644 --- a/web/src/defaults.ts +++ b/web/src/defaults.ts @@ -817,16 +817,22 @@ components: `; export const blankOverlay = `overlay: 1.1.0 -x-speakeasy-jsonpath: rfc9535 info: title: example overlay version: 0.0.0 -actions: +actions: - target: $.info.description - update: Hello World`; + description: Update the API description + update: Hello World + - target: $.components.schemas + description: Create an empty schema to copy into + update: + PetSummary: {} + - target: $.components.schemas.PetSummary + description: Copy the Pet schema into PetSummary + copy: $.components.schemas.Pet`; export const emptyOverlay = `overlay: 1.1.0 -x-speakeasy-jsonpath: rfc9535 info: title: example overlay version: 0.0.0