Skip to content

Commit

Permalink
feat: add parse to loadableNode id
Browse files Browse the repository at this point in the history
  • Loading branch information
tgriesser committed Feb 21, 2023
1 parent c32a1a0 commit 09f183e
Show file tree
Hide file tree
Showing 16 changed files with 149 additions and 26 deletions.
4 changes: 2 additions & 2 deletions packages/deno/packages/plugin-dataloader/global-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion packages/deno/packages/plugin-dataloader/refs/node.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion packages/deno/packages/plugin-dataloader/schema-builder.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions packages/deno/packages/plugin-dataloader/types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions packages/deno/packages/plugin-relay/global-types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions packages/deno/packages/plugin-relay/input-field-builder.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/plugin-dataloader/src/global-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ declare global {

loadableNodeRef: <Shape extends object, Key extends bigint | number | string, CacheKey = Key>(
name: string,
options: DataLoaderOptions<Types, Shape, Key, CacheKey> & LoadableNodeId<Types, Shape>,
options: DataLoaderOptions<Types, Shape, Key, CacheKey> & LoadableNodeId<Types, Shape, Key>,
) => ImplementableLoadableNodeRef<Types, Key | Shape, Shape, Key, CacheKey>;

loadableUnion: <
Expand All @@ -108,7 +108,10 @@ declare global {
>(
nameOrRef: NameOrRef,
options: LoadableNodeOptions<Types, Shape, Key, Interfaces, NameOrRef, CacheKey>,
) => Omit<LoadableObjectRef<Types, Key | Shape, Shape, Key, CacheKey>, 'implement'>
) => Omit<
ImplementableLoadableNodeRef<Types, Key | Shape, Shape, Key, CacheKey>,
'implement'
>
: '@pothos/plugin-relay is required to use this method';
}

Expand Down
4 changes: 3 additions & 1 deletion packages/plugin-dataloader/src/refs/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class ImplementableLoadableNodeRef<
Key extends bigint | number | string,
CacheKey,
> extends ImplementableLoadableObjectRef<Types, RefShape, Shape, Key, CacheKey> {
parseId: ((id: string, ctx: object) => Key) | undefined;
private idOptions;

constructor(
Expand All @@ -18,10 +19,11 @@ export class ImplementableLoadableNodeRef<
{
id,
...options
}: DataLoaderOptions<Types, Shape, Key, CacheKey> & LoadableNodeId<Types, Shape>,
}: DataLoaderOptions<Types, Shape, Key, CacheKey> & LoadableNodeId<Types, Shape, Key>,
) {
super(builder, name, options);
this.idOptions = id;
this.parseId = id.parse;

this.builder.configStore.onTypeConfig(this, (config) => {
const nodeInterface = (
Expand Down
8 changes: 7 additions & 1 deletion packages/plugin-dataloader/src/schema-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,13 @@ schemaBuilderProto.loadableNode = function loadableNode<
options,
);

ref.implement(options);
ref.implement({
...options,
extensions: {
...options.extensions,
pothosParseGlobalID: options.id.parse,
},
});

if (typeof nameOrRef !== 'string') {
this.configStore.associateRefWithName(nameOrRef, name);
Expand Down
8 changes: 5 additions & 3 deletions packages/plugin-dataloader/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export interface LoadableRef<K, V, C> {
getDataloader: (context: C) => DataLoader<K, V>;
}

export interface LoadableNodeId<Types extends SchemaTypes, Shape extends object> {
export interface LoadableNodeId<Types extends SchemaTypes, Shape extends object, IDShape> {
id: Omit<
FieldOptionsFromKind<
Types,
Expand All @@ -175,7 +175,9 @@ export interface LoadableNodeId<Types extends SchemaTypes, Shape extends object>
MaybePromise<OutputShape<Types, 'ID'>>
>,
'args' | 'nullable' | 'type'
>;
> & {
parse?: (id: string, ctx: Types['Context']) => IDShape;
};
}

export type LoadableNodeOptions<
Expand All @@ -186,4 +188,4 @@ export type LoadableNodeOptions<
NameOrRef extends ObjectParam<Types> | string,
CacheKey,
> = DataloaderObjectTypeOptions<Types, Shape, Key, Interfaces, NameOrRef, CacheKey> &
LoadableNodeId<Types, Shape>;
LoadableNodeId<Types, Shape, Key>;
32 changes: 32 additions & 0 deletions packages/plugin-dataloader/tests/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ interface Error {
message: String!
}
type LoadableParseTest implements Node {
id: ID!
idNumber: Int!
}
interface Node {
id: ID!
}
Expand Down Expand Up @@ -72,6 +77,8 @@ type Query {
fromContext3: User!
fromContext4: [User!]!
fromContext5: [User!]!
loadableParse: LoadableParseTest!
loadableParseNodes(ids: [ID!]): [LoadableParseTest!]!
node(id: ID!): Node
nodes(ids: [ID!]!): [Node]!
nullableUser(id: String): NullableUser
Expand Down Expand Up @@ -255,6 +262,31 @@ exports[`dataloader > queries > query with errors 1`] = `
}
`;

exports[`dataloader > queries > query with global ids 1`] = `
{
"data": {
"loadableParse": {
"id": "TG9hZGFibGVQYXJzZVRlc3Q6MQ==",
"idNumber": 1,
},
"loadableParseNodes": [
{
"id": "TG9hZGFibGVQYXJzZVRlc3Q6MQ==",
"idNumber": 1,
},
{
"id": "TG9hZGFibGVQYXJzZVRlc3Q6Mg==",
"idNumber": 2,
},
{
"id": "TG9hZGFibGVQYXJzZVRlc3Q6MTA=",
"idNumber": 10,
},
],
},
}
`;

exports[`dataloader > queries > sorts loaded results 1`] = `
{
"counts": [
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-dataloader/tests/example/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default new SchemaBuilder<{ Context: ContextType }>({
nodeQueryOptions: {},
nodesQueryOptions: {},
},
plugins: [RelayPlugin, ErrorsPlugin, DataloaderPlugin],
plugins: [ErrorsPlugin, DataloaderPlugin, RelayPlugin],
errorOptions: {
defaultTypes: [Error],
},
Expand Down
39 changes: 38 additions & 1 deletion packages/plugin-dataloader/tests/example/schema/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,38 @@ const ClassThingRef = builder.loadableNode(ClassThing, {
interfaces: [TestInterface],
id: {
resolve: (user) => user.id,
parse: (id) => id,
},
loaderOptions: { maxBatchSize: 20 },
// eslint-disable-next-line @typescript-eslint/require-await
load: async (keys: string[], context: ContextType) => [new ClassThing()],
load: async (keys, context: ContextType) => [new ClassThing()],
fields: (t) => ({}),
});

class LoadableParseTest {
readonly id: number;
constructor(id: number) {
if (typeof id !== 'number') {
throw new TypeError(`Expected id to be a number, saw ${id}`);
}
this.id = id;
}
}

const LoadableParseRef = builder.loadableNode(LoadableParseTest, {
name: 'LoadableParseTest',
id: {
resolve: (user) => user.id,
parse: (id) => Number.parseInt(id, 10),
},
loaderOptions: { maxBatchSize: 20 },
// eslint-disable-next-line @typescript-eslint/require-await
load: async (keys, context: ContextType) => keys.map((k) => new LoadableParseTest(k)),
fields: (t) => ({
idNumber: t.exposeInt('id'),
}),
});

builder.queryFields((t) => ({
userNode: t.field({
type: UserNode,
Expand Down Expand Up @@ -68,4 +93,16 @@ builder.queryFields((t) => ({
type: ClassThingRef,
resolve: () => '1',
}),
loadableParse: t.field({
type: LoadableParseRef,
resolve: () => 1,
}),
loadableParseNodes: t.field({
type: [LoadableParseRef],
args: {
ids: t.arg.globalIDList({ for: [LoadableParseRef] }),
},
// TODO: type & implement this correctly based on the "for"
resolve: (source, args) => args.ids?.map((id) => id.id) ?? [],
}),
}));
29 changes: 29 additions & 0 deletions packages/plugin-dataloader/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,35 @@ describe('dataloader', () => {
expect(result).toMatchSnapshot();
});

it('query with global ids', async () => {
const query = gql`
query {
loadableParse {
id
idNumber
}
loadableParseNodes(
ids: [
"TG9hZGFibGVQYXJzZVRlc3Q6MQ=="
"TG9hZGFibGVQYXJzZVRlc3Q6Mg=="
"TG9hZGFibGVQYXJzZVRlc3Q6MTA="
]
) {
id
idNumber
}
}
`;

const result = await execute({
schema,
document: query,
contextValue: createContext(),
});

expect(result).toMatchSnapshot();
});

it('query with errors', async () => {
const query = gql`
query {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-relay/src/global-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ declare global {
) => InputFieldRef<
InputShapeFromTypeParam<
Types,
GlobalIDInputShape<For extends NodeRef<unknown, unknown, infer T> ? T : string>,
GlobalIDInputShape<For extends { parseId?: (...args: any[]) => infer T } ? T : string>,
Req
>,
Kind
Expand All @@ -213,7 +213,7 @@ declare global {
{
[inputShapeKey]: {
typename: string;
id: For extends NodeRef<unknown, unknown, infer T> ? T : string;
id: For extends { parseId?: (...args: any[]) => infer T } ? T : string;
};
},
],
Expand Down
5 changes: 2 additions & 3 deletions packages/plugin-relay/src/input-field-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ObjectRef,
SchemaTypes,
} from '@pothos/core';
import { NodeRef } from './node-ref';
import {
GlobalIDInputFieldOptions,
GlobalIDInputShape,
Expand Down Expand Up @@ -37,7 +36,7 @@ inputFieldBuilder.globalIDList = function globalIDList<Req extends FieldRequired
(Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef<SchemaTypes>[]
)?.map((type: ObjectRef<SchemaTypes>) => ({
typename: this.builder.configStore.getTypeConfig(type).name,
parseId: type instanceof NodeRef ? type.parseId : undefined,
parseId: 'parseId' in type ? type.parseId : undefined,
})) ?? null,
},
}) as never;
Expand All @@ -60,7 +59,7 @@ inputFieldBuilder.globalID = function globalID<Req extends boolean>(
(Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef<SchemaTypes>[]
)?.map((type: ObjectRef<SchemaTypes>) => ({
typename: this.builder.configStore.getTypeConfig(type).name,
parseId: type instanceof NodeRef ? type.parseId : undefined,
parseId: 'parseId' in type ? type.parseId : undefined,
})) ?? null,
},
}) as unknown as InputFieldRef<
Expand Down

0 comments on commit 09f183e

Please sign in to comment.