Skip to content

Commit

Permalink
fix: Collections work with nested args (#3281)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker authored Nov 24, 2024
1 parent 79b9eca commit 99cd041
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 6 deletions.
9 changes: 9 additions & 0 deletions .changeset/dirty-lamps-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@data-client/endpoint': patch
'@data-client/graphql': patch
'@data-client/rest': patch
---

Collections work with nested args

This fixes [integration with qs library](https://dataclient.io/rest/api/RestEndpoint#using-qs-library) and more complex search parameters.
2 changes: 1 addition & 1 deletion packages/endpoint/src/schemas/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class CollectionSchema<
const obj =
this.argsKey ? this.argsKey(...args) : this.nestKey(parent, key);
for (const key in obj) {
if (typeof obj[key] !== 'string' && obj[key] !== undefined)
if (['number', 'boolean'].includes(typeof obj[key]))
obj[key] = `${obj[key]}`;
}
return consistentSerialize(obj);
Expand Down
25 changes: 22 additions & 3 deletions packages/endpoint/src/schemas/__tests__/Collection.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-env jest
import { initialState, State } from '@data-client/core';
import { initialState } from '@data-client/core';
import { normalize, denormalize, MemoCache } from '@data-client/normalizr';
import { IDEntity } from '__tests__/new';
import { ArticleResource, IDEntity } from '__tests__/new';
import { Record } from 'immutable';

import SimpleMemoCache from './denormalize';
Expand All @@ -12,7 +12,7 @@ import PolymorphicSchema from '../Polymorphic';
let dateSpy: jest.SpyInstance;
beforeAll(() => {
dateSpy = jest
// eslint-disable-next-line no-undef

.spyOn(global.Date, 'now')
.mockImplementation(() => new Date('2019-05-14T11:01:58.135Z').valueOf());
});
Expand Down Expand Up @@ -670,4 +670,23 @@ describe(`${schema.Collection.name} denormalization`, () => {
);
expect(queryKey).toBeUndefined();
});

it('pk should serialize differently with nested args', () => {
const filtersA = {
search: {
type: 'Coupon',
},
};
const filtersB = {
search: {
type: 'Cashback',
},
};

expect(
ArticleResource.getList.schema.pk([], undefined, '', [filtersA]),
).not.toEqual(
ArticleResource.getList.schema.pk([], undefined, '', [filtersB]),
);
});
});
2 changes: 2 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@
"@anansi/browserslist-config": "^1.4.2",
"@react-navigation/native": "^7.0.0",
"@types/node": "^22.0.0",
"@types/qs": "^6",
"@types/react": "^18.0.30",
"qs": "^6.13.1",
"react-native": "^0.76.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,32 @@ exports[`CacheProvider RestEndpoint/current should update on get for a paginated
}
`;

exports[`CacheProvider RestEndpoint/current should update on get for nested args change 1`] = `
[
Offer {
"id": "5",
"text": "hi",
},
Offer {
"id": "2",
"text": "next",
},
]
`;

exports[`CacheProvider RestEndpoint/current should update on get for nested args change 2`] = `
[
Offer {
"id": "10",
"text": "second",
},
Offer {
"id": "11",
"text": "page",
},
]
`;

exports[`CacheProvider RestEndpoint/next endpoint.assign should add to schema.Values Collections 1`] = `
Article {
"author": null,
Expand Down Expand Up @@ -400,6 +426,32 @@ exports[`CacheProvider RestEndpoint/next should update on get for a paginated re
}
`;

exports[`CacheProvider RestEndpoint/next should update on get for nested args change 1`] = `
[
Offer {
"id": "5",
"text": "hi",
},
Offer {
"id": "2",
"text": "next",
},
]
`;

exports[`CacheProvider RestEndpoint/next should update on get for nested args change 2`] = `
[
Offer {
"id": "10",
"text": "second",
},
Offer {
"id": "11",
"text": "page",
},
]
`;

exports[`CacheProvider pagination should ignore undefined values 1`] = `
[
Article {
Expand Down Expand Up @@ -759,6 +811,32 @@ exports[`ExternalDataProvider RestEndpoint/current should update on get for a pa
}
`;

exports[`ExternalDataProvider RestEndpoint/current should update on get for nested args change 1`] = `
[
Offer {
"id": "5",
"text": "hi",
},
Offer {
"id": "2",
"text": "next",
},
]
`;

exports[`ExternalDataProvider RestEndpoint/current should update on get for nested args change 2`] = `
[
Offer {
"id": "10",
"text": "second",
},
Offer {
"id": "11",
"text": "page",
},
]
`;

exports[`ExternalDataProvider RestEndpoint/next endpoint.assign should add to schema.Values Collections 1`] = `
Article {
"author": null,
Expand Down Expand Up @@ -959,6 +1037,32 @@ exports[`ExternalDataProvider RestEndpoint/next should update on get for a pagin
}
`;

exports[`ExternalDataProvider RestEndpoint/next should update on get for nested args change 1`] = `
[
Offer {
"id": "5",
"text": "hi",
},
Offer {
"id": "2",
"text": "next",
},
]
`;

exports[`ExternalDataProvider RestEndpoint/next should update on get for nested args change 2`] = `
[
Offer {
"id": "10",
"text": "second",
},
Offer {
"id": "11",
"text": "page",
},
]
`;

exports[`ExternalDataProvider pagination should ignore undefined values 1`] = `
[
Article {
Expand Down
73 changes: 72 additions & 1 deletion packages/react/src/__tests__/integration-collections.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { CacheProvider } from '@data-client/react';
import { DataProvider as ExternalDataProvider } from '@data-client/react/redux';
import { schema, RestEndpoint, PolymorphicInterface } from '@data-client/rest';
import {
schema,
RestEndpoint,
PolymorphicInterface,
RestGenerics,
Entity,
} from '@data-client/rest';
import { resource } from '@data-client/rest';
import {
IDEntity,
Expand All @@ -10,6 +16,7 @@ import {
SecondUnion,
} from '__tests__/new';
import nock from 'nock';
import qs from 'qs';

import { makeRenderDataClient, act } from '../../../test';
import { useSuspense } from '../hooks';
Expand All @@ -19,6 +26,12 @@ import {
valuesFixture,
} from '../test-fixtures';

class QSEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
searchToString(searchParams: any) {
return qs.stringify(searchParams);
}
}

class Todo extends IDEntity {
userId = 0;
title = '';
Expand Down Expand Up @@ -607,6 +620,64 @@ describe.each([
]);
});

it('should update on get for nested args change', async () => {
const filtersA = {
search: {
type: 'Coupon',
},
};
const filtersB = {
search: {
type: 'Cashback',
},
};
class Offer extends Entity {
id = '';
text = '';
}
const OfferResource = resource({
Endpoint: QSEndpoint,
schema: Offer,
searchParams: filtersA,
path: '/offers/:id',
paginationField: 'page',
});

const { result, rerender } = renderDataClient(
({ filters }) => {
return useSuspense(OfferResource.getList, filters);
},
{
initialProps: { filters: filtersA },
initialFixtures: [
{
endpoint: OfferResource.getList,
args: [filtersA],
response: [
{ id: '5', text: 'hi' },
{ id: '2', text: 'next' },
],
},
{
endpoint: OfferResource.getList,
args: [filtersB],
response: [
{ id: '10', text: 'second' },
{ id: '11', text: 'page' },
],
},
],
},
);
expect(result.current).toMatchSnapshot();
console.log(result.current);
const firstResult = result.current;
rerender({ filters: filtersB });
console.log(result.current);
expect(result.current).not.toEqual(firstResult);
expect(result.current).toMatchSnapshot();
});

it('should update on get for a paginated resource with searchParams', async () => {
mynock.get(`/article`).reply(200, paginatedFirstPage);
mynock.get(`/article?userId=2`).reply(200, paginatedFirstPage);
Expand Down
20 changes: 19 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3189,7 +3189,9 @@ __metadata:
"@data-client/use-enhanced-reducer": "npm:^0.1.10"
"@react-navigation/native": "npm:^7.0.0"
"@types/node": "npm:^22.0.0"
"@types/qs": "npm:^6"
"@types/react": "npm:^18.0.30"
qs: "npm:^6.13.1"
react-native: "npm:^0.76.0"
peerDependencies:
"@react-navigation/native": ^6.0.0 || ^7.0.0
Expand Down Expand Up @@ -7491,6 +7493,13 @@ __metadata:
languageName: node
linkType: hard

"@types/qs@npm:^6":
version: 6.9.17
resolution: "@types/qs@npm:6.9.17"
checksum: 10c0/a183fa0b3464267f8f421e2d66d960815080e8aab12b9aadab60479ba84183b1cdba8f4eff3c06f76675a8e42fe6a3b1313ea76c74f2885c3e25d32499c17d1b
languageName: node
linkType: hard

"@types/range-parser@npm:*":
version: 1.2.4
resolution: "@types/range-parser@npm:1.2.4"
Expand Down Expand Up @@ -25016,7 +25025,7 @@ __metadata:
languageName: node
linkType: hard

"qs@npm:6.13.0, qs@npm:^6.12.3":
"qs@npm:6.13.0":
version: 6.13.0
resolution: "qs@npm:6.13.0"
dependencies:
Expand All @@ -25025,6 +25034,15 @@ __metadata:
languageName: node
linkType: hard

"qs@npm:^6.12.3, qs@npm:^6.13.1":
version: 6.13.1
resolution: "qs@npm:6.13.1"
dependencies:
side-channel: "npm:^1.0.6"
checksum: 10c0/5ef527c0d62ffca5501322f0832d800ddc78eeb00da3b906f1b260ca0492721f8cdc13ee4b8fd8ac314a6ec37b948798c7b603ccc167e954088df392092f160c
languageName: node
linkType: hard

"qs@npm:~6.5.2":
version: 6.5.3
resolution: "qs@npm:6.5.3"
Expand Down

0 comments on commit 99cd041

Please sign in to comment.