Skip to content

Commit

Permalink
fix: query option in gen2 sdks to correctly flatten the query objec…
Browse files Browse the repository at this point in the history
…t for mongodb (#3250)

## Description

This PR updates the query option to use `flattenMongoQuery` which is
basically flatten but when we get an `$key` we just return the object as
is as, so that it properly gets parsed in our API (mongo query).

**Jira**
https://builder-io.atlassian.net/browse/ENG-5268

**Loom**
https://www.loom.com/share/10493e4fd2a24b40937590f7f690bff2

Fixes #3007
  • Loading branch information
sidmohanty11 committed May 1, 2024
1 parent ab6e5dc commit 70fccea
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 4 deletions.
11 changes: 11 additions & 0 deletions .changeset/spicy-cobras-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@builder.io/sdk-react-nextjs': patch
'@builder.io/sdk-qwik': patch
'@builder.io/sdk-react': patch
'@builder.io/sdk-react-native': patch
'@builder.io/sdk-solid': patch
'@builder.io/sdk-svelte': patch
'@builder.io/sdk-vue': patch
---

Fix: `query` option correctly flattens mongodb queries
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ exports[`Generate Content URL > generate content url with apiVersion as default

exports[`Generate Content URL > generate content url with apiVersion as v3 1`] = `"https://cdn.builder.io/api/v3/content/page?apiKey=YJIGb4i01jvw0SRdL5Bt&limit=30&noTraverse=true&includeRefs=true&omit=meta.componentsUsed&cachebust=true&noCache=true&overrides.037948e52eaf4743afed464f02c70da4=037948e52eaf4743afed464f02c70da4&overrides.page=037948e52eaf4743afed464f02c70da4&overrides.page%3A%2F=037948e52eaf4743afed464f02c70da4&preview=page&query.id=%22c1b81bab59704599b997574eb0736def%22"`;

exports[`Generate Content URL > generate content url with correct mongoQuery with $ in child key 1`] = `"https://cdn.builder.io/api/v3/content/page?apiKey=YJIGb4i01jvw0SRdL5Bt&limit=30&noTraverse=true&includeRefs=true&omit=meta.componentsUsed&query.some.key=%7B%22%24elemMatch%22%3A%7B%22some.nested.key%22%3A%7B%22%24in%22%3A%5B%22value1%22%2C%22value2%22%5D%7D%7D%7D"`;

exports[`Generate Content URL > generate content url with correct mongoQuery with $and as the root key 1`] = `"https://cdn.builder.io/api/v3/content/page?apiKey=YJIGb4i01jvw0SRdL5Bt&limit=30&noTraverse=true&includeRefs=true&omit=meta.componentsUsed&query=%7B%22%24and%22%3A%5B%7B%22some.key%22%3A%7B%22%24elemMatch%22%3A%7B%22some.nested.key%22%3A%7B%22%24in%22%3A%5B%22value1%22%2C%22value2%22%5D%7D%7D%7D%7D%2C%7B%22some.other.key%22%3A%7B%22%24eq%22%3A%22value3%22%7D%7D%5D%7D"`;

exports[`Generate Content URL > generate content url with enrich option not present 1`] = `"https://cdn.builder.io/api/v3/content/page?apiKey=YJIGb4i01jvw0SRdL5Bt&limit=30&noTraverse=true&includeRefs=true&omit=meta.componentsUsed"`;

exports[`Generate Content URL > generate content url with enrich option true 1`] = `"https://cdn.builder.io/api/v3/content/page?apiKey=YJIGb4i01jvw0SRdL5Bt&limit=30&noTraverse=true&includeRefs=true&enrich=true&omit=meta.componentsUsed"`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,47 @@ describe('Generate Content URL', () => {
});
expect(output).toMatchSnapshot();
});

test('generate content url with correct mongoQuery with $and as the root key', () => {
const output = generateContentUrl({
apiKey: testKey,
model: testModel,
query: {
$and: [
{
'some.key': {
$elemMatch: {
'some.nested.key': {
$in: ['value1', 'value2'],
},
},
},
},
{
'some.other.key': {
$eq: 'value3',
},
},
],
},
});
expect(output).toMatchSnapshot();
});

test('generate content url with correct mongoQuery with $ in child key', () => {
const output = generateContentUrl({
apiKey: testKey,
model: testModel,
query: {
'some.key': {
$elemMatch: {
'some.nested.key': {
$in: ['value1', 'value2'],
},
},
},
},
});
expect(output).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { flatten } from '../../helpers/flatten.js';
import { flatten, flattenMongoQuery } from '../../helpers/flatten.js';
import { normalizeSearchParams } from '../../helpers/search/search.js';
import { DEFAULT_API_VERSION } from '../../types/api-version.js';
import { getBuilderSearchParamsFromWindow } from '../get-builder-search-params/index.js';
Expand Down Expand Up @@ -96,11 +96,10 @@ export const generateContentUrl = (options: GetContentOptions): URL => {
url.searchParams.set('userAttributes', JSON.stringify(userAttributes));
}
if (query) {
const flattened = flatten({ query });
const flattened = flattenMongoQuery({ query });
for (const key in flattened) {
url.searchParams.set(key, JSON.stringify((flattened as any)[key]));
url.searchParams.set(key, JSON.stringify(flattened[key]));
}
}

return url;
};
29 changes: 29 additions & 0 deletions packages/sdks/src/helpers/flatten.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,32 @@ export function flatten<T extends Record<string, any>>(
: { ...acc, [newPath]: value };
}, {} as T);
}

/**
* Flatten a nested MongoDB query object into a flat object with dot-separated keys.
* $ keys are not flattened and are left as is.
*
* { foo: { bar: { $gt: 5 }}} -> { 'foo.bar': { '$gt': 5 }}
* { foo: {'bar.id': { $elemMatch: { 'baz.id': { $in: ['abc', 'bcd'] }}}}} -> { 'foo.bar.id': { '$elemMatch': { 'baz.id': { '$in': ['abc', 'bcd'] }}}}
*/
export function flattenMongoQuery(
obj: any,
_current?: any,
_res: any = {}
): { [key: string]: string } {
for (const key in obj) {
const value = obj[key];
const newKey = _current ? _current + '.' + key : key;
if (
value &&
typeof value === 'object' &&
!Array.isArray(value) &&
!Object.keys(value).find((item) => item.startsWith('$'))
) {
flattenMongoQuery(value, newKey, _res);
} else {
_res[newKey] = value;
}
}
return _res;
}

0 comments on commit 70fccea

Please sign in to comment.