Skip to content

Commit

Permalink
Support nested lists in add-graphql plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
hayes committed Oct 9, 2024
1 parent afdce2b commit e6ca3fa
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 50 deletions.
5 changes: 5 additions & 0 deletions .changeset/tender-deers-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pothos/core": minor
---

Fix a couple incorrect generics in plugin system types
5 changes: 5 additions & 0 deletions .changeset/yellow-moose-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pothos/plugin-add-graphql": minor
---

Support nested lists
6 changes: 4 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import { UnionRef as InternalUnionRef } from './refs/union';
import type {
AddVersionedDefaultsToBuilderOptions,
FieldKind,
InputTypeParam,
NormalizeSchemeBuilderOptions,
RootName,
SchemaTypes,
TypeParam,
} from './types';

export * from './errors';
Expand Down Expand Up @@ -158,7 +160,7 @@ export const InputObjectRef = InternalInputObjectRef as new <Types extends Schem

export type InputListRef<Types extends SchemaTypes, T> = PothosSchemaTypes.InputListRef<Types, T>;
export const InputListRef = InternalInputListRef as new <Types extends SchemaTypes, T>(
name: string,
listType: InputTypeParam<Types>,
required: boolean,
) => PothosSchemaTypes.InputListRef<Types, T>;

Expand Down Expand Up @@ -197,7 +199,7 @@ export const UnionRef = InternalUnionRef as new <Types extends SchemaTypes, T, P

export type ListRef<Types extends SchemaTypes, T, P = T> = PothosSchemaTypes.ListRef<Types, T, P>;
export const ListRef = InternalListRef as new <Types extends SchemaTypes, T, P = T>(
name: string,
listType: TypeParam<Types>,
nullable: boolean,
) => PothosSchemaTypes.ListRef<Types, T, P>;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/refs/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { BaseTypeRef } from './base';

export class EnumRef<Types extends SchemaTypes, T, U = T>
extends BaseTypeRef<Types, PothosEnumTypeConfig>
implements OutputRef, InputRef, PothosSchemaTypes.EnumRef<Types, T, U>
implements OutputRef<T>, InputRef<U>, PothosSchemaTypes.EnumRef<Types, T, U>
{
override kind = 'Enum' as const;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/refs/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TypeRefWithFields } from './base-with-fields';

export class InterfaceRef<Types extends SchemaTypes, T, P = T>
extends TypeRefWithFields<Types, PothosInterfaceTypeConfig>
implements OutputRef, PothosSchemaTypes.InterfaceRef<Types, T, P>
implements OutputRef<T>, PothosSchemaTypes.InterfaceRef<Types, T, P>
{
override kind = 'Interface' as const;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/refs/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type ObjectLikeConfig =
| PothosSubscriptionTypeConfig;
export class ObjectRef<Types extends SchemaTypes, T, P = T>
extends TypeRefWithFields<Types, ObjectLikeConfig>
implements OutputRef, PothosSchemaTypes.ObjectRef<Types, T, P>
implements OutputRef<T>, PothosSchemaTypes.ObjectRef<Types, T, P>
{
override kind = 'Object' as const;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/refs/scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { BaseTypeRef } from './base';

export class ScalarRef<Types extends SchemaTypes, T, U, P = T>
extends BaseTypeRef<Types, PothosScalarTypeConfig>
implements OutputRef, InputRef, PothosSchemaTypes.ScalarRef<Types, T, U, P>
implements OutputRef<T>, InputRef<U>, PothosSchemaTypes.ScalarRef<Types, T, U, P>
{
override kind = 'Scalar' as const;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/refs/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BaseTypeRef } from './base';

export class UnionRef<Types extends SchemaTypes, T, P = T>
extends BaseTypeRef<Types, PothosUnionTypeConfig>
implements OutputRef, PothosSchemaTypes.UnionRef<Types, T, P>
implements OutputRef<T>, PothosSchemaTypes.UnionRef<Types, T, P>
{
override kind = 'Union' as const;

Expand Down
82 changes: 39 additions & 43 deletions packages/plugin-add-graphql/src/schema-builder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import './global-types';
import SchemaBuilder, {
InputListRef,
ListRef,
type ArgumentRef,
type EnumRef,
type EnumValueConfigMap,
Expand All @@ -17,13 +19,11 @@ import {
type GraphQLInputObjectType,
type GraphQLInputType,
type GraphQLInterfaceType,
type GraphQLNamedInputType,
type GraphQLNamedOutputType,
type GraphQLObjectType,
type GraphQLOutputType,
type GraphQLType,
type GraphQLUnionType,
defaultFieldResolver,
getNamedType,
isListType,
isNonNullType,
} from 'graphql';
Expand All @@ -39,73 +39,69 @@ import { addReferencedType } from './utils';

const proto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;

function resolveOutputTypeRef(
function resolveNullableOutputRef(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLNamedOutputType,
) {
type: GraphQLOutputType,
): OutputType<SchemaTypes> {
if (isNonNullType(type)) {
throw new Error('Expected a nullable type');
}

if (isListType(type)) {
const nullable = !isNonNullType(type.ofType);
const listType = nullable ? type.ofType : type.ofType.ofType;

return new ListRef(resolveNullableOutputRef(builder, listType), nullable);
}

addReferencedType(builder, type);

return type.name as OutputType<SchemaTypes>;
}

function resolveInputTypeRef(
function resolveNullableInputRef(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLNamedInputType,
) {
addReferencedType(builder, type);
type: GraphQLType,
): InputTypeParam<SchemaTypes> {
if (isNonNullType(type)) {
throw new Error('Expected a nullable type');
}

if (isListType(type)) {
const required = isNonNullType(type.ofType);
const listType = required ? type.ofType.ofType : type.ofType;
return new InputListRef(resolveNullableInputRef(builder, listType), required);
}

addReferencedType(builder, type);
return type.name as InputType<SchemaTypes>;
}

function resolveOutputType(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLOutputType,
): { type: TypeParam<SchemaTypes>; nullable: boolean } {
const namedType = getNamedType(type);
const isNullable = !isNonNullType(type);
const nonNullable = isNonNullType(type) ? type.ofType : type;
const isList = isListType(nonNullable);
const typeRef = resolveOutputTypeRef(builder, namedType);

if (!isList) {
return {
type: typeRef,
nullable: isNullable,
};
}
const typeRef = resolveNullableOutputRef(builder, nonNullable);

return {
type: [typeRef],
nullable: {
list: isNullable,
items: !isNonNullType(nonNullable.ofType),
} as unknown as boolean,
type: typeRef,
nullable: isNullable,
};
}

function resolveInputType(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLInputType,
): { type: InputTypeParam<SchemaTypes>; required: boolean } {
const namedType = getNamedType(type);
const isNullable = !isNonNullType(type);
const nonNullable = isNonNullType(type) ? type.ofType : type;
const isList = isListType(nonNullable);
const typeRef = resolveInputTypeRef(builder, namedType);

if (!isList) {
return {
type: typeRef,
required: !isNullable,
};
}
const typeRef = resolveNullableInputRef(builder, nonNullable);

return {
type: [typeRef],
required: {
list: !isNullable,
items: isNonNullType(nonNullable.ofType),
} as unknown as boolean,
type: typeRef,
required: !isNullable,
};
}

Expand All @@ -118,7 +114,7 @@ proto.addGraphQLObject = function addGraphQLObject<Shape>(
description: type.description ?? undefined,
isTypeOf: type.isTypeOf as never,
extensions: { ...type.extensions, ...extensions },
interfaces: () => type.getInterfaces().map((i) => resolveOutputTypeRef(this, i)) as [],
interfaces: () => type.getInterfaces().map((i) => resolveNullableOutputRef(this, i)) as [],
fields: (t: PothosSchemaTypes.ObjectFieldBuilder<SchemaTypes, Shape>) => {
const existingFields = type.getFields();
const newFields = fields?.(t) ?? {};
Expand Down Expand Up @@ -188,7 +184,7 @@ proto.addGraphQLInterface = function addGraphQLInterface<Shape = unknown>(
description: type.description ?? undefined,
resolveType: type.resolveType as never,
extensions: { ...type.extensions, ...extensions },
interfaces: () => type.getInterfaces().map((i) => resolveOutputTypeRef(this, i)) as [],
interfaces: () => type.getInterfaces().map((i) => resolveNullableOutputRef(this, i)) as [],
fields: (t) => {
const existingFields = type.getFields();
const newFields = fields?.(t) ?? {};
Expand Down Expand Up @@ -246,7 +242,7 @@ proto.addGraphQLUnion = function addGraphQLUnion<Shape>(
description: type.description ?? undefined,
resolveType: type.resolveType as never,
extensions: { ...type.extensions, ...extensions },
types: types ?? (type.getTypes().map((t) => resolveOutputTypeRef(this, t)) as []),
types: types ?? (type.getTypes().map((t) => resolveNullableOutputRef(this, t)) as []),
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ const User = new GraphQLObjectType<{ id: string; name: string; profile?: { bio?:
type: new GraphQLList(new GraphQLNonNull(Post)),
resolve: () => [{ id: '123', title: 'title', content: 'content' }],
},
nestedPosts: {
args: {
ids: {
type: new GraphQLList(new GraphQLNonNull(new GraphQLList(GraphQLID))),
},
},
type: new GraphQLList(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Post)))),
},
}),
});

Expand Down
1 change: 1 addition & 0 deletions packages/plugin-add-graphql/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('simple objects example schema', () => {
"type AddedUser implements Node {
id: ID!
name: String
nestedPosts(ids: [[ID]!]): [[Post!]!]
posts: [Post!]
profile: Profile
}
Expand Down

0 comments on commit e6ca3fa

Please sign in to comment.