-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: hackweek * chore: tinker * chore: cleanup * chore: add the jsonpath-compliance-test-suite * chore: implement more stuff * chore: parse filter expressions * chore: add functions into tokenizer * chore: parse filter expressions * chore: evaluate * chore: basic evaluation * chore: make more private * chore: work through compliance suite * continuing to tinker * chore: support basic unions * chore: basic eval * chore: standard filters * chore: filter matches * chore: more conformance * chore: more conformance * chore: more conformance * chore: slice sanitization * chore: conformance * chore: fix slices * chore: type validation * chore: fixup functions * chore: pass conformance test * chore: clean up tests after conformance test * chore: remove unnecessary comment * chore: bridge, service workers * chore: commentary * chore: allow superceding messages * chore: setup defaults * chore: add wasm to git
- Loading branch information
1 parent
69a55f2
commit 2869aa7
Showing
41 changed files
with
7,337 additions
and
663 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.idea | ||
dist | ||
dist | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "jsonpath-compliance-test-suite"] | ||
path = jsonpath-compliance-test-suite | ||
url = [email protected]:jsonpath-standard/jsonpath-compliance-test-suite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
//go:build js && wasm | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/speakeasy-api/jsonpath/pkg/overlay" | ||
"gopkg.in/yaml.v3" | ||
"syscall/js" | ||
) | ||
|
||
func CalculateOverlay(originalYAML, targetYAML string) (string, error) { | ||
var orig yaml.Node | ||
err := yaml.Unmarshal([]byte(originalYAML), &orig) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse source schema: %w", err) | ||
} | ||
var target yaml.Node | ||
err = yaml.Unmarshal([]byte(targetYAML), &target) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse target schema: %w", err) | ||
} | ||
|
||
overlay, err := overlay.Compare("example overlay", &orig, target) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to compare schemas: %w", err) | ||
} | ||
out, err := yaml.Marshal(overlay) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to marshal schema: %w", err) | ||
} | ||
|
||
return string(out), nil | ||
} | ||
|
||
func ApplyOverlay(originalYAML, overlayYAML string) (string, error) { | ||
var orig yaml.Node | ||
err := yaml.Unmarshal([]byte(originalYAML), &orig) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse original schema: %w", err) | ||
} | ||
|
||
var overlay overlay.Overlay | ||
err = yaml.Unmarshal([]byte(overlayYAML), &overlay) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse overlay schema: %w", err) | ||
} | ||
|
||
err = overlay.ApplyTo(&orig) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to apply overlay: %w", err) | ||
} | ||
|
||
// Unwrap the document node if it exists and has only one content node | ||
if orig.Kind == yaml.DocumentNode && len(orig.Content) == 1 { | ||
orig = *orig.Content[0] | ||
} | ||
|
||
out, err := yaml.Marshal(&orig) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to marshal result: %w", err) | ||
} | ||
|
||
return string(out), nil | ||
} | ||
|
||
func promisify(fn func(args []js.Value) (string, error)) js.Func { | ||
return js.FuncOf(func(this js.Value, args []js.Value) any { | ||
// Handler for the Promise | ||
handler := js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} { | ||
resolve := promiseArgs[0] | ||
reject := promiseArgs[1] | ||
|
||
// Run this code asynchronously | ||
go func() { | ||
result, err := fn(args) | ||
if err != nil { | ||
errorConstructor := js.Global().Get("Error") | ||
errorObject := errorConstructor.New(err.Error()) | ||
reject.Invoke(errorObject) | ||
return | ||
} | ||
|
||
resolve.Invoke(result) | ||
}() | ||
|
||
// The handler of a Promise doesn't return any value | ||
return nil | ||
}) | ||
|
||
// Create and return the Promise object | ||
promiseConstructor := js.Global().Get("Promise") | ||
return promiseConstructor.New(handler) | ||
}) | ||
} | ||
|
||
func main() { | ||
js.Global().Set("CalculateOverlay", promisify(func(args []js.Value) (string, error) { | ||
if len(args) != 2 { | ||
return "", fmt.Errorf("CalculateOverlay: expected 2 args, got %v", len(args)) | ||
} | ||
|
||
return CalculateOverlay(args[0].String(), args[1].String()) | ||
})) | ||
|
||
js.Global().Set("ApplyOverlay", promisify(func(args []js.Value) (string, error) { | ||
if len(args) != 2 { | ||
return "", fmt.Errorf("ApplyOverlay: expected 2 args, got %v", len(args)) | ||
} | ||
|
||
return ApplyOverlay(args[0].String(), args[1].String()) | ||
})) | ||
|
||
<-make(chan bool) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package jsonpath_test | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/pmezard/go-difflib/difflib" | ||
"github.com/speakeasy-api/jsonpath/pkg/jsonpath" | ||
"github.com/stretchr/testify/require" | ||
"gopkg.in/yaml.v3" | ||
"os" | ||
"slices" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
type FullTestSuite struct { | ||
Description string `json:"description"` | ||
Tests []Test `json:"tests"` | ||
} | ||
type Test struct { | ||
Name string `json:"name"` | ||
Selector string `json:"selector"` | ||
Document interface{} `json:"document"` | ||
Result []interface{} `json:"result"` | ||
Results [][]interface{} `json:"results"` | ||
InvalidSelector bool `json:"invalid_selector"` | ||
Tags []string `json:"tags"` | ||
} | ||
|
||
func TestJSONPathComplianceTestSuite(t *testing.T) { | ||
// Read the test suite JSON file | ||
file, err := os.ReadFile("./jsonpath-compliance-test-suite/cts.json") | ||
require.NoError(t, err, "Failed to read test suite file") | ||
// alter the file to delete any unicode tests: these break the yaml library we use.. | ||
var testSuite FullTestSuite | ||
json.Unmarshal(file, &testSuite) | ||
for i := 0; i < len(testSuite.Tests); i++ { | ||
// if Tags contains "unicode", delete it | ||
shouldDelete := slices.Contains(testSuite.Tests[i].Tags, "unicode") | ||
// delete new line / some unicode tests -- these break the yaml parser | ||
shouldDelete = shouldDelete || strings.Contains(testSuite.Tests[i].Name, "line feed") | ||
shouldDelete = shouldDelete || strings.Contains(testSuite.Tests[i].Name, "carriage return") | ||
shouldDelete = shouldDelete || strings.Contains(testSuite.Tests[i].Name, "u2028") | ||
shouldDelete = shouldDelete || strings.Contains(testSuite.Tests[i].Name, "u2029") | ||
if shouldDelete { | ||
testSuite.Tests = append(testSuite.Tests[:i], testSuite.Tests[i+1:]...) | ||
i-- | ||
} | ||
} | ||
|
||
// Run each test case as a subtest | ||
for _, test := range testSuite.Tests { | ||
t.Run(test.Name, func(t *testing.T) { | ||
// Test case for a valid selector | ||
jp, err := jsonpath.NewPath(test.Selector) | ||
if test.InvalidSelector { | ||
require.Error(t, err, "Expected an error for invalid selector, but got none") | ||
return | ||
} else { | ||
require.NoError(t, err, "Failed to parse JSONPath selector") | ||
} | ||
// interface{} to yaml.Node | ||
toYAML := func(i interface{}) *yaml.Node { | ||
o, err := yaml.Marshal(i) | ||
require.NoError(t, err, "Failed to marshal interface to yaml") | ||
n := new(yaml.Node) | ||
err = yaml.Unmarshal(o, n) | ||
require.NoError(t, err, "Failed to unmarshal yaml to yaml.Node") | ||
// unwrap the document node | ||
if n.Kind == yaml.DocumentNode && len(n.Content) == 1 { | ||
n = n.Content[0] | ||
} | ||
return n | ||
} | ||
|
||
result := jp.Query(toYAML(test.Document)) | ||
|
||
if test.Results != nil { | ||
expectedResults := make([][]*yaml.Node, 0) | ||
for _, expectedResult := range test.Results { | ||
expected := make([]*yaml.Node, 0) | ||
for _, expectedResult := range expectedResult { | ||
expected = append(expected, toYAML(expectedResult)) | ||
} | ||
expectedResults = append(expectedResults, expected) | ||
} | ||
|
||
// Test case with multiple possible results | ||
var found bool | ||
for i, _ := range test.Results { | ||
if match, msg := compareResults(result, expectedResults[i]); match { | ||
found = true | ||
break | ||
} else { | ||
t.Log(msg) | ||
} | ||
} | ||
if !found { | ||
t.Errorf("Unexpected result. Got: %v, Want one of: %v", result, test.Results) | ||
} | ||
} else { | ||
expectedResult := make([]*yaml.Node, 0) | ||
for _, res := range test.Result { | ||
expectedResult = append(expectedResult, toYAML(res)) | ||
} | ||
// Test case with a single expected result | ||
if match, msg := compareResults(result, expectedResult); !match { | ||
t.Error(msg) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func compareResults(actual, expected []*yaml.Node) (bool, string) { | ||
actualStr, err := yaml.Marshal(actual) | ||
if err != nil { | ||
return false, "Failed to serialize actual result: " + err.Error() | ||
} | ||
|
||
expectedStr, err := yaml.Marshal(expected) | ||
if err != nil { | ||
return false, "Failed to serialize expected result: " + err.Error() | ||
} | ||
|
||
if string(actualStr) == string(expectedStr) { | ||
return true, "" | ||
} | ||
|
||
// Use a differ library to generate a nice diff string | ||
// You can use a package like github.com/pmezard/go-difflib/difflib | ||
diff := difflib.UnifiedDiff{ | ||
A: difflib.SplitLines(string(expectedStr)), | ||
B: difflib.SplitLines(string(actualStr)), | ||
FromFile: "Expected", | ||
ToFile: "Actual", | ||
Context: 3, | ||
} | ||
diffStr, err := difflib.GetUnifiedDiffString(diff) | ||
if err != nil { | ||
return false, "Failed to generate diff: " + err.Error() | ||
} | ||
|
||
return false, diffStr | ||
} |
Submodule jsonpath-compliance-test-suite
added at
927770
Oops, something went wrong.