Skip to content

Commit

Permalink
enhance: Queries passthrough suspense rather than converting to undef…
Browse files Browse the repository at this point in the history
…ined
  • Loading branch information
ntucker committed Apr 25, 2024
1 parent 5469db0 commit 67064c7
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 29 deletions.
11 changes: 11 additions & 0 deletions .changeset/short-experts-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@data-client/endpoint": patch
"@data-client/rest": patch
"@data-client/graphql": patch
---

Queries pass-through suspense rather than ever being undefined

- [useSuspense()](https://dataclient.io/docs/api/useSuspense) return values will not be nullable
- [useQuery()](https://dataclient.io/docs/api/useQuery) will still be nullable due to it handling `INVALID` as `undefined` return
- [Query.process](https://dataclient.io/rest/api/Query#process) does not need to handle nullable cases
22 changes: 18 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ jobs:
- image: cimg/node:20.12
resource_class: large
steps:
- checkout
- checkout:
depth: 1
- run:
name: examples use local packages
command: |
Expand All @@ -37,8 +38,21 @@ jobs:
- persist_to_workspace:
root: ~/
paths:
- project

# excplitily list so we can ignore some directories that are not needed
- project/packages
- project/examples
- project/node_modules
- project/__tests__
- project/scripts
- project/babel.config.js
- project/jest.config.js
- project/tsconfig-base.json
- project/tsconfig.json
- project/yarn.lock
- project/tsconfig.test.json
- project/package.json
- project/.yarnrc.yml
- project/.yarn
lint:
docker: *docker
resource_class: medium+
Expand Down Expand Up @@ -80,7 +94,7 @@ jobs:
if [ "<< parameters.react-version >>" == "^17.0.0" ]; then
curl -Os https://uploader.codecov.io/latest/linux/codecov;
chmod +x codecov;
yarn run test:coverage --ci --maxWorkers=3 --coverageReporters=text-lcov > ./lcov.info;
yarn run test:coverage --ci --maxWorkers=4 --coverageReporters=text-lcov > ./lcov.info;
if [ "$CODECOV_TOKEN" != "" ]; then
./codecov -t ${CODECOV_TOKEN} < ./lcov.info || true;
else
Expand Down
2 changes: 1 addition & 1 deletion docs/rest/api/Collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export const getPosts = new RestEndpoint({
nonFilterArgumentKeys: /orderBy/,
}),
(posts, { orderBy } = {}) => {
if (orderBy && posts) {
if (orderBy) {
return [...posts].sort((a, b) => a[orderBy].localeCompare(b[orderBy]));
}
return posts;
Expand Down
2 changes: 1 addition & 1 deletion docs/rest/api/Query.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const getPosts = new RestEndpoint({
nonFilterArgumentKeys: /orderBy/,
}),
(posts, { orderBy } = {}) => {
if (orderBy && posts) {
if (orderBy) {
return [...posts].sort((a, b) =>
a[orderBy].localeCompare(b[orderBy]),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/controller/__tests__/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ describe('Controller.get()', () => {
entities,
};
const tacoCount = new schema.Query(TacoList, tacos => {
return tacos?.length ?? 0;
return tacos.length ?? 0;
});

expect(controller.get(tacoCount, { type: 'foo' }, state)).toBe(1);
Expand Down
4 changes: 3 additions & 1 deletion packages/endpoint/src/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ export class All<
| (S extends EntityMap ? UnionResult<S> : Normalize<S>)[]
| undefined;

_denormalizeNullable(): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
_denormalizeNullable():
| (S extends EntityMap<infer T> ? T : Denormalize<S>)[]
| undefined;

denormalize(
input: {},
Expand Down
7 changes: 4 additions & 3 deletions packages/endpoint/src/schemas/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
SchemaSimple,
} from '../interface.js';
import type {
Denormalize,
DenormalizeNullable,
NormalizeNullable,
SchemaArgs,
Expand All @@ -17,7 +18,7 @@ import type {
*/
export default class Query<
S extends Queryable,
P extends (entries: DenormalizeNullable<S>, ...args: any) => any,
P extends (entries: Denormalize<S>, ...args: any) => any,
> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>>
{
declare schema: S;
Expand All @@ -32,9 +33,9 @@ export default class Query<
return (this.schema as any).normalize(...args);
}

denormalize(input: {}, args: any, unvisit: any): ReturnType<P> | undefined {
denormalize(input: {}, args: any, unvisit: any): ReturnType<P> {
const value = unvisit(input, this.schema);
return typeof value === 'symbol' ? undefined : this.process(value, ...args);
return typeof value === 'symbol' ? value : this.process(value, ...args);
}

queryKey(
Expand Down
62 changes: 61 additions & 1 deletion packages/endpoint/src/schemas/__tests__/Query.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// eslint-env jest
import { MemoCache } from '@data-client/normalizr';
import { useQuery, useSuspense } from '@data-client/react';
import { RestEndpoint } from '@data-client/rest';
import { IDEntity } from '__tests__/new';
import { fromJS } from 'immutable';

Expand Down Expand Up @@ -188,7 +190,6 @@ describe('top level schema', () => {
const sortedUsers = new schema.Query(
new schema.Collection([User]),
(results, { asc } = { asc: false }, ...args) => {
if (!results) return results;
const sorted = [...results].sort((a, b) => a.name.localeCompare(b.name));
if (asc) return sorted;
return sorted.reverse();
Expand All @@ -214,6 +215,49 @@ describe('top level schema', () => {
expect(users).toMatchSnapshot();
});

test('works if base entity suspends', () => {
const entities = {
User: {
1: { id: '1', name: 'Milo' },
2: { id: '2', name: 'Jake' },
3: { id: '3', name: 'Zeta' },
4: { id: '4', name: 'Alpha' },
},
};
const users = new MemoCache().query('', sortedUsers, [], entities, {});
expect(users).toBeUndefined();
});

test('works if base entity suspends', () => {
const allSortedUsers = new schema.Query(
new schema.All(User),
(results, { asc } = { asc: false }, ...args) => {
const sorted = [...results].sort((a, b) =>
a.name.localeCompare(b.name),
);
if (asc) return sorted;
return sorted.reverse();
},
);
const users = new MemoCache().query('', allSortedUsers, [], {}, {});
expect(users).toBeUndefined();
});

test('works with nested schemas', () => {
const allSortedUsers = new schema.Query(
new schema.All(User),
(results, { asc } = { asc: false }, ...args) => {
const sorted = [...results].sort((a, b) =>
a.name.localeCompare(b.name),
);
if (asc) return sorted;
return sorted.reverse();
},
);
const users = new MemoCache().query('', allSortedUsers, [], {}, {});
expect(users).toBeUndefined();
});

test('denormalizes should not be found when no entities are present', () => {
const entities = {
DOG: {
Expand All @@ -226,4 +270,20 @@ describe('top level schema', () => {

expect(value).toEqual(undefined);
});

test('', () => {
const getUsers = new RestEndpoint({
path: '/users',
searchParams: {} as { asc?: boolean },
schema: sortedUsers,
});
() => {
const users = useSuspense(getUsers, { asc: true });
users.map(user => user.name);
const others = useQuery(getUsers.schema, { asc: true });
// @ts-expect-error
others.map(user => user.name);
others?.map(user => user.name);
};
});
});
2 changes: 1 addition & 1 deletion packages/rest/src/__tests__/createResource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ describe('createResource()', () => {

const queryRemainingTodos = new schema.Query(
TodoResource.getList.schema,
entries => entries && entries.filter(todo => !todo.completed).length,
entries => entries.filter(todo => !todo.completed).length,
);

() => useQuery(queryRemainingTodos, { userId: 1 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,12 @@ declare class Invalidate<E extends EntityInterface & {
*
* @see https://dataclient.io/rest/api/Query
*/
declare class Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
declare class Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
schema: S;
process: P;
constructor(schema: S, process: P);
normalize(...args: any): any;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P> | undefined;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P>;
queryKey(args: ProcessParameters<P, S>, queryKey: (schema: any, args: any, getEntity: GetEntity, getIndex: GetIndex) => any, getEntity: GetEntity, getIndex: GetIndex): any;
_denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType<P> | undefined;
_normalizeNullable: () => NormalizeNullable<S>;
Expand Down Expand Up @@ -636,7 +636,9 @@ declare class All<
| (S extends EntityMap ? UnionResult<S> : Normalize<S>)[]
| undefined;

_denormalizeNullable(): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
_denormalizeNullable():
| (S extends EntityMap<infer T> ? T : Denormalize<S>)[]
| undefined;

denormalize(
input: {},
Expand Down Expand Up @@ -915,7 +917,7 @@ type schema_d_Invalidate<E extends EntityInterface & {
process: any;
}> = Invalidate<E>;
declare const schema_d_Invalidate: typeof Invalidate;
type schema_d_Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> = Query<S, P>;
type schema_d_Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> = Query<S, P>;
declare const schema_d_Query: typeof Query;
type schema_d_SchemaClass<T = any, N = T | undefined, Args extends any[] = any[]> = SchemaClass<T, N, Args>;
type schema_d_All<S extends EntityMap | EntityInterface = EntityMap | EntityInterface> = All<S>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,12 @@ declare class Invalidate<E extends EntityInterface & {
*
* @see https://dataclient.io/rest/api/Query
*/
declare class Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
declare class Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
schema: S;
process: P;
constructor(schema: S, process: P);
normalize(...args: any): any;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P> | undefined;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P>;
queryKey(args: ProcessParameters<P, S>, queryKey: (schema: any, args: any, getEntity: GetEntity, getIndex: GetIndex) => any, getEntity: GetEntity, getIndex: GetIndex): any;
_denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType<P> | undefined;
_normalizeNullable: () => NormalizeNullable<S>;
Expand Down Expand Up @@ -636,7 +636,9 @@ declare class All<
| (S extends EntityMap ? UnionResult<S> : Normalize<S>)[]
| undefined;

_denormalizeNullable(): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
_denormalizeNullable():
| (S extends EntityMap<infer T> ? T : Denormalize<S>)[]
| undefined;

denormalize(
input: {},
Expand Down Expand Up @@ -915,7 +917,7 @@ type schema_d_Invalidate<E extends EntityInterface & {
process: any;
}> = Invalidate<E>;
declare const schema_d_Invalidate: typeof Invalidate;
type schema_d_Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> = Query<S, P>;
type schema_d_Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> = Query<S, P>;
declare const schema_d_Query: typeof Query;
type schema_d_SchemaClass<T = any, N = T | undefined, Args extends any[] = any[]> = SchemaClass<T, N, Args>;
type schema_d_All<S extends EntityMap | EntityInterface = EntityMap | EntityInterface> = All<S>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,12 @@ declare class Invalidate<E extends EntityInterface & {
*
* @see https://dataclient.io/rest/api/Query
*/
declare class Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
declare class Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
schema: S;
process: P;
constructor(schema: S, process: P);
normalize(...args: any): any;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P> | undefined;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P>;
queryKey(args: ProcessParameters<P, S>, queryKey: (schema: any, args: any, getEntity: GetEntity, getIndex: GetIndex) => any, getEntity: GetEntity, getIndex: GetIndex): any;
_denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType<P> | undefined;
_normalizeNullable: () => NormalizeNullable<S>;
Expand Down Expand Up @@ -634,7 +634,9 @@ declare class All<
| (S extends EntityMap ? UnionResult<S> : Normalize<S>)[]
| undefined;

_denormalizeNullable(): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
_denormalizeNullable():
| (S extends EntityMap<infer T> ? T : Denormalize<S>)[]
| undefined;

denormalize(
input: {},
Expand Down Expand Up @@ -913,7 +915,7 @@ type schema_d_Invalidate<E extends EntityInterface & {
process: any;
}> = Invalidate<E>;
declare const schema_d_Invalidate: typeof Invalidate;
type schema_d_Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> = Query<S, P>;
type schema_d_Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> = Query<S, P>;
declare const schema_d_Query: typeof Query;
type schema_d_SchemaClass<T = any, N = T | undefined, Args extends any[] = any[]> = SchemaClass<T, N, Args>;
type schema_d_All<S extends EntityMap | EntityInterface = EntityMap | EntityInterface> = All<S>;
Expand Down
10 changes: 6 additions & 4 deletions website/src/components/Playground/editor-types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,12 @@ declare class Invalidate<E extends EntityInterface & {
*
* @see https://dataclient.io/rest/api/Query
*/
declare class Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
declare class Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> implements SchemaSimple<ReturnType<P> | undefined, ProcessParameters<P, S>> {
schema: S;
process: P;
constructor(schema: S, process: P);
normalize(...args: any): any;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P> | undefined;
denormalize(input: {}, args: any, unvisit: any): ReturnType<P>;
queryKey(args: ProcessParameters<P, S>, queryKey: (schema: any, args: any, getEntity: GetEntity, getIndex: GetIndex) => any, getEntity: GetEntity, getIndex: GetIndex): any;
_denormalizeNullable: (input: {}, args: readonly any[], unvisit: (input: any, schema: any) => any) => ReturnType<P> | undefined;
_normalizeNullable: () => NormalizeNullable<S>;
Expand Down Expand Up @@ -638,7 +638,9 @@ declare class All<
| (S extends EntityMap ? UnionResult<S> : Normalize<S>)[]
| undefined;

_denormalizeNullable(): (S extends EntityMap<infer T> ? T : Denormalize<S>)[];
_denormalizeNullable():
| (S extends EntityMap<infer T> ? T : Denormalize<S>)[]
| undefined;

denormalize(
input: {},
Expand Down Expand Up @@ -917,7 +919,7 @@ type schema_d_Invalidate<E extends EntityInterface & {
process: any;
}> = Invalidate<E>;
declare const schema_d_Invalidate: typeof Invalidate;
type schema_d_Query<S extends Queryable, P extends (entries: DenormalizeNullable<S>, ...args: any) => any> = Query<S, P>;
type schema_d_Query<S extends Queryable, P extends (entries: Denormalize<S>, ...args: any) => any> = Query<S, P>;
declare const schema_d_Query: typeof Query;
type schema_d_SchemaClass<T = any, N = T | undefined, Args extends any[] = any[]> = SchemaClass<T, N, Args>;
type schema_d_All<S extends EntityMap | EntityInterface = EntityMap | EntityInterface> = All<S>;
Expand Down

0 comments on commit 67064c7

Please sign in to comment.