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
187 changes: 187 additions & 0 deletions packages/host/tests/unit/query-field-normalization-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,193 @@ module('normalizeQueryDefinition', function () {
assert.strictEqual(normalized?.realm, 'https://other.realm/');
});

test('injects on into leaf filter inside not', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: { not: { eq: { name: 'foo' } } },
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
assert.deepEqual(normalized?.query.filter, {
not: { eq: { name: 'foo' }, on: targetRef },
});
});

test('injects on into each leaf filter inside any', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: {
any: [{ eq: { name: 'foo' } }, { contains: { title: 'bar' } }],
},
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
assert.deepEqual(normalized?.query.filter, {
any: [
{ eq: { name: 'foo' }, on: targetRef },
{ contains: { title: 'bar' }, on: targetRef },
],
});
});

test('injects on into each leaf filter inside every', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: {
every: [{ eq: { name: 'foo' } }, { range: { age: { gte: 18 } } }],
},
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
assert.deepEqual(normalized?.query.filter, {
every: [
{ eq: { name: 'foo' }, on: targetRef },
{ range: { age: { gte: 18 } }, on: targetRef },
],
});
});

test('injects on into deeply nested combinator filters', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: {
every: [
{
any: [{ eq: { name: 'foo' } }, { eq: { name: 'bar' } }],
},
{ not: { contains: { title: 'baz' } } },
],
},
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
assert.deepEqual(normalized?.query.filter, {
every: [
{
any: [
{ eq: { name: 'foo' }, on: targetRef },
{ eq: { name: 'bar' }, on: targetRef },
],
},
{ not: { contains: { title: 'baz' }, on: targetRef } },
],
});
});

test('skips type filters inside combinators', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);
let typeRef = { module: 'https://example.com/other', name: 'Other' };

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: {
any: [{ eq: { name: 'foo' } }, { type: typeRef }],
},
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
assert.deepEqual(normalized?.query.filter, {
any: [{ eq: { name: 'foo' }, on: targetRef }, { type: typeRef }],
});
});

test('does not overwrite existing on in leaf filters', function (assert) {
let realmURL = new URL('https://realm.example/');
let relativeTo = new URL('https://realm.example/cards/1');
let existingOn = { module: 'https://example.com/custom', name: 'Custom' };

let normalized = normalizeQueryDefinition({
fieldDefinition,
queryDefinition: {
filter: {
any: [
{ eq: { name: 'foo' }, on: existingOn },
{ eq: { name: 'bar' } },
],
},
},
realmURL,
fieldName: 'testField',
resolvePathValue: () => undefined,
relativeTo,
});

assert.ok(normalized, 'normalization succeeded');
let targetRef = codeRefWithAbsoluteURL(
fieldDefinition.fieldOrCard,
relativeTo,
);
assert.deepEqual(normalized?.query.filter, {
any: [
{ eq: { name: 'foo' }, on: existingOn },
{ eq: { name: 'bar' }, on: targetRef },
],
});
});

test('resolves live instances via custom path resolver', function (assert) {
let realmURL = new URL('https://realm.example/');
let instance = { address: { city: 'Paris' } };
Expand Down
36 changes: 34 additions & 2 deletions packages/runtime-common/query-field-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ export function normalizeQueryDefinition({
let filter = queryAny.filter as Record<string, any> | undefined;
if (!filter || Object.keys(filter).length === 0) {
queryAny.filter = { type: targetRef };
} else if (!filter.on) {
filter.on = targetRef;
} else {
injectOnIntoLeafFilters(filter, targetRef);
}

if (Array.isArray(queryAny.sort)) {
Expand Down Expand Up @@ -315,6 +315,38 @@ export function buildQuerySearchURL(realmHref: string, query: Query): string {
return searchURL.href;
}

function injectOnIntoLeafFilters(
filter: Record<string, any>,
targetRef: any,
): void {
if ('type' in filter) {
return;
}
if ('not' in filter && filter.not && typeof filter.not === 'object') {
injectOnIntoLeafFilters(filter.not, targetRef);
return;
}
if ('any' in filter && Array.isArray(filter.any)) {
for (let child of filter.any) {
if (child && typeof child === 'object') {
injectOnIntoLeafFilters(child, targetRef);
}
}
return;
}
if ('every' in filter && Array.isArray(filter.every)) {
for (let child of filter.every) {
if (child && typeof child === 'object') {
injectOnIntoLeafFilters(child, targetRef);
}
}
return;
}
if (!filter.on) {
filter.on = targetRef;
}
}

export function cloneRelationship(
relationship?: Relationship,
): Relationship | undefined {
Expand Down
Loading