Skip to content

Commit

Permalink
adust tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Jul 19, 2023
1 parent b559c9d commit 31e7a77
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 42 deletions.
8 changes: 5 additions & 3 deletions docs/graphcache/local-directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ If you want to add directives yourself you can do so by performing
cacheExchange({
directives: {
// If you now add `@_pagination` to your document we will execute this
pagination: () => {},
pagination: directiveArguments => () => {
/* Resolver */
},
},
});
```

The function signature of a directive is the same as the one of a [Resolver](./local-directives.md). In
case you need to access the arguments you have passed to a directive you can do so by checking `info.directiveArguments`.
The function signature of a directive is a function which receives the arguments the directive is called with in the document.
That function should returns a [Resolver](./local-directives.md).

### Reading on

Expand Down
7 changes: 5 additions & 2 deletions exchanges/graphcache/src/extras/relayPagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { it, expect, describe } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
import { Store } from '../store/store';
import { relayPagination } from './relayPagination';
import { MergeMode, relayPagination } from './relayPagination';

function itemNode(numItem: number) {
return {
Expand Down Expand Up @@ -1566,7 +1566,10 @@ describe('as directive', () => {

const store = new Store({
directives: {
relayPagination: relayPagination(),
relayPagination: directiveArguments =>
relayPagination({
mergeMode: directiveArguments!.mergeMode as MergeMode,
}),
},
});

Expand Down
77 changes: 75 additions & 2 deletions exchanges/graphcache/src/extras/simplePagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { it, expect, describe } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
import { Store } from '../store/store';
import { simplePagination } from './simplePagination';
import { MergeMode, simplePagination } from './simplePagination';

describe('as resolver', () => {
it('works with forward pagination', () => {
Expand Down Expand Up @@ -453,7 +453,7 @@ describe('as directive', () => {

const store = new Store({
directives: {
simplePagination: simplePagination(),
simplePagination: simplePagination,
},
});

Expand Down Expand Up @@ -508,4 +508,77 @@ describe('as directive', () => {
});
expect(pageThreeResult.data).toEqual(null);
});

it('works with backwards pagination', () => {
const Pagination = gql`
query ($skip: Number, $limit: Number) {
__typename
persons(skip: $skip, limit: $limit)
@_simplePagination(mergeMode: "before") {
__typename
id
name
}
}
`;

const store = new Store({
directives: {
simplePagination: directiveArguments =>
simplePagination({
mergeMode: directiveArguments!.mergeMode as MergeMode,
}),
},
});

const pageOne = {
__typename: 'Query',
persons: [
{ id: 7, name: 'Jovi', __typename: 'Person' },
{ id: 8, name: 'Phil', __typename: 'Person' },
{ id: 9, name: 'Andy', __typename: 'Person' },
],
};

const pageTwo = {
__typename: 'Query',
persons: [
{ id: 4, name: 'Kadi', __typename: 'Person' },
{ id: 5, name: 'Dom', __typename: 'Person' },
{ id: 6, name: 'Sofia', __typename: 'Person' },
],
};

write(
store,
{ query: Pagination, variables: { skip: 0, limit: 3 } },
pageOne
);
const pageOneResult = query(store, {
query: Pagination,
variables: { skip: 0, limit: 3 },
});
expect(pageOneResult.data).toEqual(pageOne);

write(
store,
{ query: Pagination, variables: { skip: 3, limit: 3 } },
pageTwo
);

const pageTwoResult = query(store, {
query: Pagination,
variables: { skip: 3, limit: 3 },
});
expect((pageTwoResult.data as any).persons).toEqual([
...pageTwo.persons,
...pageOne.persons,
]);

const pageThreeResult = query(store, {
query: Pagination,
variables: { skip: 6, limit: 3 },
});
expect(pageThreeResult.data).toEqual(null);
});
});
37 changes: 27 additions & 10 deletions exchanges/graphcache/src/operations/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
FieldNode,
DocumentNode,
FragmentDefinitionNode,
DirectiveNode,
} from '@0no-co/graphql.web';

import {
Expand All @@ -16,6 +17,7 @@ import {
getMainOperation,
normalizeVariables,
getFieldArguments,
getDirectives,
} from '../ast';

import {
Expand All @@ -25,6 +27,7 @@ import {
Link,
OperationRequest,
Dependencies,
Directive,
} from '../types';

import { joinKeys, keyOfField } from '../store/keys';
Expand Down Expand Up @@ -295,6 +298,20 @@ export const _queryFragment = (
return result;
};

function getFieldDirective(
node: FormattedNode<FieldNode>,
store: Store
): { storeDirective: Directive; fieldDirective: DirectiveNode } | undefined {
const directives = getDirectives(node);
for (const name in directives) {
if (name !== 'include' && name !== 'skip' && store.directives[name])
return {
storeDirective: store.directives[name],
fieldDirective: directives[name]!,
};
}
}

const readSelection = (
ctx: Context,
key: string,
Expand Down Expand Up @@ -356,8 +373,7 @@ const readSelection = (
let node: FormattedNode<FieldNode> | void;
const output = InMemoryData.makeData(input);
while ((node = iterate()) !== undefined) {
const fieldDirectives = Object.keys(node._directives || {}).map(x => x);
const storeDirective = fieldDirectives.find(x => store.directives[x]);
const foundDirective = getFieldDirective(node, store);

// Derive the needed data from our node.
const fieldName = getName(node);
Expand All @@ -376,7 +392,7 @@ const readSelection = (
ctx.__internal.path.push(fieldAlias);
// We temporarily store the data field in here, but undefined
// means that the value is missing from the cache
let dataFieldValue: void | DataField;
let dataFieldValue: void | DataField = undefined;

if (fieldName === '__typename') {
// We directly assign the typename as it's already available
Expand All @@ -386,7 +402,7 @@ const readSelection = (
dataFieldValue = resultValue;
} else if (
InMemoryData.currentOperation === 'read' &&
((resolvers && resolvers[fieldName]) || storeDirective)
((resolvers && resolvers[fieldName]) || foundDirective)
) {
// We have to update the information in context to reflect the info
// that the resolver will receive
Expand All @@ -398,7 +414,7 @@ const readSelection = (
output[fieldAlias] = fieldValue;
}

if (resolvers && resolvers[fieldName] && storeDirective) {
if (resolvers && resolvers[fieldName] && foundDirective) {
warn(
`A resolver and directive is being used at "${typename}.${fieldName}", only the directive will apply.`,
28
Expand All @@ -412,11 +428,12 @@ const readSelection = (
store,
ctx
);
} else if (storeDirective) {
const fieldDirective = node._directives![storeDirective];
const directiveArguments =
getFieldArguments(fieldDirective, ctx.variables) || {};
dataFieldValue = store.directives[storeDirective]!(directiveArguments)(
} else {
const directiveArguments = getFieldArguments(
foundDirective!.fieldDirective,
ctx.variables
);
dataFieldValue = foundDirective!.storeDirective(directiveArguments)(
output,
fieldArgs || ({} as Variables),
store,
Expand Down
4 changes: 2 additions & 2 deletions exchanges/graphcache/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ type DocumentNode = TypedDocumentNode<any, any>;
type RootField = 'query' | 'mutation' | 'subscription';

const defaultDirectives: DirectivesConfig = {
optional: (_parent, args, cache, info) => {
optional: () => (_parent, args, cache, info) => {
const result = cache.resolve(info.parentFieldKey, info.fieldName, args);
return result === undefined ? null : result;
},
required: (_parent, args, cache, info) => {
required: () => (_parent, args, cache, info) => {
const result = cache.resolve(info.parentFieldKey, info.fieldName, args);
return result === null ? undefined : result;
},
Expand Down
27 changes: 4 additions & 23 deletions exchanges/graphcache/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,29 +684,6 @@ export type Resolver<
): Result;
}['bivarianceHack'];

/** Cache Directive, which may resolve or replace data during cache reads.
*
* @param parent - The GraphQL object that is currently being constructed from cache data.
* @param args - This field’s arguments.
* @param cache - {@link Cache} interface.
* @param info - {@link ResolveInfo} interface.
* @returns a {@link ResolverResult}, which is an updated value, partial entity, or entity key
*
* @see {@link https://urql.dev/goto/docs/graphcache/local-directives} for the full directives docs.
*/
export type Directive<
ParentData = DataFields,
Args = Variables,
Result = ResolverResult
> = {
bivarianceHack(
parent: ParentData,
args: Args,
cache: Cache,
info: ResolveInfo & { directiveArguments: Record<string, any> }
): Result;
}['bivarianceHack'];

/** Configures resolvers which replace cached reuslts with custom values.
*
* @remarks
Expand All @@ -724,6 +701,10 @@ export type ResolverConfig = {
} | void;
};

export type Directive = (
directiveArguments: Record<string, unknown> | null
) => Resolver;

export type DirectivesConfig = {
[directiveName: string]: Directive;
};
Expand Down

0 comments on commit 31e7a77

Please sign in to comment.