Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for enum values to @pgQuery #786

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/graphile-utils/__tests__/ExtendSchemaPlugin-pg.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -836,3 +836,49 @@ it("allows to retrieve array scalar values", async () => {
pgClient.release();
}
});

it("allows to retrieve enum values", async () => {
const schema = await createPostGraphileSchema(pgPool, ["graphile_utils"], {
disableDefaultMutations: true,
appendPlugins: [
makeExtendSchemaPlugin(build => {
const { pgSql: sql } = build;
return {
typeDefs: gql`
enum Enum {
ENUM
}
extend type User {
myCustomEnum: Enum! @pgQuery(
fragment: ${embed(sql.fragment`'ENUM'::text`)}
)
}
`,
};
}),
],
});
expect(schema).toMatchSnapshot();
const pgClient = await pgPool.connect();
try {
const { data, errors } = await graphql(
schema,
`
query {
user: userById(id: 1) {
myCustomEnum
}
}
`,
null,
{ pgClient },
{}
);
expect(errors).toBeFalsy();
expect(data).toBeTruthy();
expect(data.user).toBeTruthy();
expect(data.user.myCustomEnum).toEqual("ENUM");
} finally {
pgClient.release();
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -3434,3 +3434,296 @@ input ComplexInput {
}

`;

exports[`allows to retrieve enum values 1`] = `
"""The root query type which gives access points into the data universe."""
type Query implements Node {
"""
Exposes the root query type nested one level down. This is helpful for Relay 1
which can only query top level fields if they are in a particular form.
"""
query: Query!

"""
The root query type must be a \`Node\` to work well with Relay 1 mutations. This just resolves to \`query\`.
"""
nodeId: ID!

"""Fetches an object given its globally unique \`ID\`."""
node(
"""The globally unique \`ID\`."""
nodeId: ID!
): Node

"""Reads and enables pagination through a set of \`Pet\`."""
allPets(
"""Only read the first \`n\` values of the set."""
first: Int

"""Only read the last \`n\` values of the set."""
last: Int

"""
Skip the first \`n\` values from our \`after\` cursor, an alternative to cursor
based pagination. May not be used with \`last\`.
"""
offset: Int

"""Read all values in the set before (above) this cursor."""
before: Cursor

"""Read all values in the set after (below) this cursor."""
after: Cursor

"""The method to use when ordering \`Pet\`."""
orderBy: [PetsOrderBy!] = [PRIMARY_KEY_ASC]

"""
A condition to be used in determining which values should be returned by the collection.
"""
condition: PetCondition
): PetsConnection

"""Reads and enables pagination through a set of \`User\`."""
allUsers(
"""Only read the first \`n\` values of the set."""
first: Int

"""Only read the last \`n\` values of the set."""
last: Int

"""
Skip the first \`n\` values from our \`after\` cursor, an alternative to cursor
based pagination. May not be used with \`last\`.
"""
offset: Int

"""Read all values in the set before (above) this cursor."""
before: Cursor

"""Read all values in the set after (below) this cursor."""
after: Cursor

"""The method to use when ordering \`User\`."""
orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC]

"""
A condition to be used in determining which values should be returned by the collection.
"""
condition: UserCondition
): UsersConnection
petById(id: Int!): Pet
userById(id: Int!): User

"""Reads a single \`Pet\` using its globally unique \`ID\`."""
pet(
"""The globally unique \`ID\` to be used in selecting a single \`Pet\`."""
nodeId: ID!
): Pet

"""Reads a single \`User\` using its globally unique \`ID\`."""
user(
"""The globally unique \`ID\` to be used in selecting a single \`User\`."""
nodeId: ID!
): User
}

"""An object with a globally unique \`ID\`."""
interface Node {
"""
A globally unique identifier. Can be used in various places throughout the system to identify this single value.
"""
nodeId: ID!
}

"""A connection to a list of \`Pet\` values."""
type PetsConnection {
"""A list of \`Pet\` objects."""
nodes: [Pet]!

"""
A list of edges which contains the \`Pet\` and cursor to aid in pagination.
"""
edges: [PetsEdge!]!

"""Information to aid in pagination."""
pageInfo: PageInfo!

"""The count of *all* \`Pet\` you could get from the connection."""
totalCount: Int!
}

type Pet implements Node {
"""
A globally unique identifier. Can be used in various places throughout the system to identify this single value.
"""
nodeId: ID!
id: Int!
userId: Int!
name: String!
type: String!
}

"""A \`Pet\` edge in the connection."""
type PetsEdge {
"""A cursor for use in pagination."""
cursor: Cursor

"""The \`Pet\` at the end of the edge."""
node: Pet
}

"""A location in a connection that can be used for resuming pagination."""
scalar Cursor

"""Information about pagination in a connection."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!

"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!

"""When paginating backwards, the cursor to continue."""
startCursor: Cursor

"""When paginating forwards, the cursor to continue."""
endCursor: Cursor
}

"""Methods to use when ordering \`Pet\`."""
enum PetsOrderBy {
NATURAL
ID_ASC
ID_DESC
USER_ID_ASC
USER_ID_DESC
NAME_ASC
NAME_DESC
TYPE_ASC
TYPE_DESC
PRIMARY_KEY_ASC
PRIMARY_KEY_DESC
}

"""
A condition to be used against \`Pet\` object types. All fields are tested for equality and combined with a logical ‘and.’
"""
input PetCondition {
"""Checks for equality with the object’s \`id\` field."""
id: Int

"""Checks for equality with the object’s \`userId\` field."""
userId: Int

"""Checks for equality with the object’s \`name\` field."""
name: String

"""Checks for equality with the object’s \`type\` field."""
type: String
}

"""A connection to a list of \`User\` values."""
type UsersConnection {
"""A list of \`User\` objects."""
nodes: [User]!

"""
A list of edges which contains the \`User\` and cursor to aid in pagination.
"""
edges: [UsersEdge!]!

"""Information to aid in pagination."""
pageInfo: PageInfo!

"""The count of *all* \`User\` you could get from the connection."""
totalCount: Int!
}

type User implements Node {
"""
A globally unique identifier. Can be used in various places throughout the system to identify this single value.
"""
nodeId: ID!
id: Int!
name: String!
email: String!
bio: String
renamedComplexColumn: [Complex]
createdAt: Datetime!
myCustomEnum: Enum!
}

type Complex {
numberInt: Int
stringText: String
}

"""
A point in time as described by the [ISO
8601](https://en.wikipedia.org/wiki/ISO_8601) standard. May or may not include a timezone.
"""
scalar Datetime

enum Enum {
ENUM
}

"""A \`User\` edge in the connection."""
type UsersEdge {
"""A cursor for use in pagination."""
cursor: Cursor

"""The \`User\` at the end of the edge."""
node: User
}

"""Methods to use when ordering \`User\`."""
enum UsersOrderBy {
NATURAL
ID_ASC
ID_DESC
NAME_ASC
NAME_DESC
EMAIL_ASC
EMAIL_DESC
BIO_ASC
BIO_DESC
RENAMED_COMPLEX_COLUMN_ASC
RENAMED_COMPLEX_COLUMN_DESC
CREATED_AT_ASC
CREATED_AT_DESC
PRIMARY_KEY_ASC
PRIMARY_KEY_DESC
}

"""
A condition to be used against \`User\` object types. All fields are tested for equality and combined with a logical ‘and.’
"""
input UserCondition {
"""Checks for equality with the object’s \`id\` field."""
id: Int

"""Checks for equality with the object’s \`name\` field."""
name: String

"""Checks for equality with the object’s \`email\` field."""
email: String

"""Checks for equality with the object’s \`bio\` field."""
bio: String

"""Checks for equality with the object’s \`renamedComplexColumn\` field."""
renamedComplexColumn: [ComplexInput]

"""Checks for equality with the object’s \`createdAt\` field."""
createdAt: Datetime
}

"""An input for mutations affecting \`Complex\`"""
input ComplexInput {
numberInt: Int
stringText: String
}

`;
8 changes: 4 additions & 4 deletions packages/graphile-utils/src/makeExtendSchemaPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ export default function makeExtendSchemaPlugin(
const Self: GraphQLNamedType = SelfGeneric as any;
const {
pgSql: sql,
graphql: { isScalarType, getNamedType },
graphql: { isLeafType, getNamedType },
} = build;
function augmentResolver(
resolver: AugmentedGraphQLFieldResolver<TSource, any>,
Expand Down Expand Up @@ -885,13 +885,13 @@ export default function makeExtendSchemaPlugin(
scope.pgFieldIntrospection.kind === "class"
? scope.pgFieldIntrospection
: null;
const isScalar = isScalarType(getNamedType(type));
const isLeaf = isLeafType(getNamedType(type));

const generateImplicitResolverIfPossible = () => {
if (
directives.pgQuery &&
((table && directives.pgQuery.source) ||
(isScalar && directives.pgQuery.fragment))
(isLeaf && directives.pgQuery.fragment))
) {
return (
data: any,
Expand Down Expand Up @@ -1062,7 +1062,7 @@ export default function makeExtendSchemaPlugin(
};
}
);
} else if (isScalar && directives.pgQuery.fragment) {
} else if (isLeaf && directives.pgQuery.fragment) {
fieldContext.addDataGenerator(
(parsedResolveInfoFragment: any) => {
return {
Expand Down