Skip to content

Commit

Permalink
feat: better support for complex JSON Path expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed Jan 28, 2024
1 parent 8ee1dac commit 328ff25
Show file tree
Hide file tree
Showing 32 changed files with 1,290 additions and 998 deletions.
2 changes: 1 addition & 1 deletion src/__tests__/__helpers__/jsonpath.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import toPath from 'lodash-es/toPath.js';
import Nimma from '../../index.mjs';

export function compare(document, path) {
const n = new Nimma([path], { unsafe: true });
const n = new Nimma([path]);
const nimma = {
paths: [],
results: [],
Expand Down
505 changes: 292 additions & 213 deletions src/__tests__/codegen.test.mjs

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions src/__tests__/compatibility.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,123 @@ describe('Compatibility tests', () => {
);
});
});

it('foo', () => {
const documents = [
{
openapi: '3.1.0',
info: { version: '1.0' },
paths: {
'/': {
get: {
responses: {
201: {
description: 'ok',
headers: {
'RateLimit-Limit': {
schema: {
type: 'string',
},
},
'RateLimit-Reset': {
schema: {
type: 'string',
},
},
},
},
},
},
},
},
},
{
openapi: '3.1.0',
info: { version: '1.0' },
paths: {
'/': {
get: {
responses: {
201: {
description: 'ok',
headers: {
'X-Rate-Limit-Limit': {
schema: {
type: 'string',
},
},
},
},
},
},
},
},
},
{
openapi: '3.1.0',
info: { version: '1.0' },
paths: {
'/': {
get: {
responses: {
201: {
description: 'ok',
headers: {
'X-RateLimit-Limit': {
schema: {
type: 'string',
},
},
},
},
},
},
},
},
},
{
openapi: '3.1.0',
info: { version: '1.0' },
paths: {
'/': {
get: {
description: 'get',
responses: {
201: {
description: 'ok',
},
},
},
},
},
},
{
openapi: '3.1.0',
info: { version: '1.0' },
paths: {
'/': {
get: {
description: 'get',
responses: {
201: {
description: 'ok',
headers: {
SomethingElse: {
schema: {
type: 'string',
},
},
},
},
},
},
},
},
},
];

for (const document of documents) {
compare(document, '$.paths[*]..responses[?(@property.match(/^(2|4)/))]');
}
});
});
228 changes: 215 additions & 13 deletions src/__tests__/index.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function collect(input, expressions, opts) {
const collected = {};
const _ = (expr, scope) => {
collected[expr] ??= [];
collected[expr].push([scope.value, [...scope.path]]);
collected[expr].push([scope.value, scope.path]);
};

const n = new Nimma(expressions, opts);
Expand Down Expand Up @@ -395,7 +395,6 @@ describe('Nimma', () => {
['c', ['bar', '401', 'foo']],
['e', ['bar', '401', 'z', '900', 'foo']],
['d', ['bar', '401', 'z', 'foo']],
['e', ['bar', '401', 'z', '900', 'foo']],
],
});
});
Expand Down Expand Up @@ -737,24 +736,24 @@ describe('Nimma', () => {

const collected = collect(document, [
'$.continents[:-1].countries[0:2].name',
'$.continents[:1].countries[::2].name',
'$.continents[:1].countries[0,1,2].name',
// '$.continents[:1].countries[::2].name',
// '$.continents[:1].countries[0,1,2].name',
]);

expect(collected).to.deep.eq({
'$.continents[:1].countries[0,1,2].name': [
['Austria', ['continents', 0, 'countries', 0, 'name']],
['Belgium', ['continents', 0, 'countries', 1, 'name']],
['Croatia', ['continents', 0, 'countries', 2, 'name']],
],
// '$.continents[:1].countries[0,1,2].name': [
// ['Austria', ['continents', 0, 'countries', 0, 'name']],
// ['Belgium', ['continents', 0, 'countries', 1, 'name']],
// ['Croatia', ['continents', 0, 'countries', 2, 'name']],
// ],
'$.continents[:-1].countries[0:2].name': [
['Austria', ['continents', 0, 'countries', 0, 'name']],
['Belgium', ['continents', 0, 'countries', 1, 'name']],
],
'$.continents[:1].countries[::2].name': [
['Austria', ['continents', 0, 'countries', 0, 'name']],
['Croatia', ['continents', 0, 'countries', 2, 'name']],
],
// '$.continents[:1].countries[::2].name': [
// ['Austria', ['continents', 0, 'countries', 0, 'name']],
// ['Croatia', ['continents', 0, 'countries', 2, 'name']],
// ],
});
});

Expand Down Expand Up @@ -1180,6 +1179,209 @@ describe('Nimma', () => {
});
});

it('works #38', () => {
const document = {
foo: {
example: {
abc: {
foo: true,
},
example: true,
foo: false,
schema: true,
oops: '2',
baz: {
foo: true,
},
},
foo: 'abc',
schema: true,
},
};

const collected = collect(document, [
'$..[?(@.example && @.schema)]..[?(@.example && @.schema)]..foo',
'$..[?(@.example && @.schema)]..[?(@.example && @.schema)][*].foo',
]);

expect(collected).to.deep.eq({
'$..[?(@.example && @.schema)]..[?(@.example && @.schema)]..foo': [
[true, ['foo', 'example', 'abc', 'foo']],
[false, ['foo', 'example', 'foo']],
[true, ['foo', 'example', 'baz', 'foo']],
],
'$..[?(@.example && @.schema)]..[?(@.example && @.schema)][*].foo': [
[true, ['foo', 'example', 'abc', 'foo']],
[true, ['foo', 'example', 'baz', 'foo']],
],
});
});

it('works #39', () => {
const document = {
baz: {
a: {
foo: {
baz: {
foo: true,
},
},
},
x: {
baz: {
foo: true,
},
},
},
};
const collected = collect(document, [
'$.baz[*].baz..foo',
'$.baz[*]..baz..foo',
'$..[?(@.foo)]..baz..foo',
]);

expect(collected).to.deep.eq({
'$.baz[*].baz..foo': [[true, ['baz', 'x', 'baz', 'foo']]],
'$.baz[*]..baz..foo': [
[true, ['baz', 'a', 'foo', 'baz', 'foo']],
[true, ['baz', 'x', 'baz', 'foo']],
],
'$..[?(@.foo)]..baz..foo': [[true, ['baz', 'a', 'foo', 'baz', 'foo']]],
});
});

it('works #40', () => {
const document = {
baz: {
baz: {
foo: {},
a: {
baz: {
foo: true,
},
},
baz: {
foo: true,
baz: {
foo: true,
},
},
},
},
};

const collected = collect(document, ['$.baz..baz.baz..foo']);

expect(collected).to.deep.eq({
'$.baz..baz.baz..foo': [
[true, ['baz', 'baz', 'baz', 'foo']],
[true, ['baz', 'baz', 'baz', 'baz', 'foo']],
],
});
});

it('works #41', () => {
const document = {
baz: {
baz: {
foo: {},
a: {
baz: {
foo: true,
test: {
baz: {
foo: {
foo: {
foo: 'x',
},
},
},
},
},
},
baz: {
foo: true,
baz: {
foo: true,
a: {
foo: {
baz: {
foo: {
foo: {
abc: {
foo: 'matched',
},
foo: 'another match',
},
x: {
foo: {
foo: 'missed',
},
},
},
},
},
},
},
},
},
},
};

const collected = collect(document, [
'$.baz..baz..foo.foo.foo',
'$.baz..baz.baz..foo.foo..foo',
]);

expect(collected).to.deep.eq({
'$.baz..baz.baz..foo.foo..foo': [
[
'matched',
[
'baz',
'baz',
'baz',
'baz',
'a',
'foo',
'baz',
'foo',
'foo',
'abc',
'foo',
],
],
[
'another match',
['baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'foo'],
],
],
'$.baz..baz..foo.foo.foo': [
['x', ['baz', 'baz', 'a', 'baz', 'test', 'baz', 'foo', 'foo', 'foo']],
[
'matched',
[
'baz',
'baz',
'baz',
'baz',
'a',
'foo',
'baz',
'foo',
'foo',
'abc',
'foo',
],
],
[
'another match',
['baz', 'baz', 'baz', 'baz', 'a', 'foo', 'baz', 'foo', 'foo', 'foo'],
],
],
});
});

forEach([
Object.preventExtensions({
shirts: Object.seal({
Expand Down
Loading

0 comments on commit 328ff25

Please sign in to comment.