Skip to content

Commit

Permalink
feat(graphql): implement Node for top level query field
Browse files Browse the repository at this point in the history
  • Loading branch information
calebmer committed Oct 10, 2016
1 parent a485700 commit 594b554
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ enum PostStatus {
}
# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -319,6 +319,9 @@ type Query {
# 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\`.
__id: ID!
}
input UpdatePersonByEmailInput {
Expand Down
24 changes: 20 additions & 4 deletions src/graphql/schema/getQueryGQLType.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { GraphQLObjectType, GraphQLFieldConfig, GraphQLNonNull } from 'graphql'
import { GraphQLObjectType, GraphQLFieldConfig, GraphQLNonNull, GraphQLID } from 'graphql'
import { buildObject, memoize1 } from '../utils'
import createNodeFieldEntry from './node/createNodeFieldEntry'
import getNodeInterfaceType from './node/getNodeInterfaceType'
import createCollectionQueryFieldEntries from './collection/createCollectionQueryFieldEntries'
import BuildToken from './BuildToken'

export const $$isQuery = Symbol('isQuery')

// TODO: doc
const getGQLQueryType = memoize1(createGQLQueryType)

export default getGQLQueryType

// TODO: doc
function createGQLQueryType (buildToken: BuildToken): GraphQLObjectType<mixed> {
const { inventory } = buildToken
const { options, inventory } = buildToken
let queryType: GraphQLObjectType<mixed>

queryType = new GraphQLObjectType({
queryType = new GraphQLObjectType<mixed>({
name: 'Query',
description: 'The root query type which gives access points into the data universe.',
interfaces: [getNodeInterfaceType(buildToken)],
// A value in our system is the value of this query type if there is no parent type
// (i.e. it is the root type), or the value is the symbol `$$isQuery`.
isTypeOf: (value, context, info) => info.parentType == null || value === $$isQuery,
fields: () => buildObject<GraphQLFieldConfig<mixed, mixed>>(
[
createNodeFieldEntry(buildToken),
Expand All @@ -30,10 +37,19 @@ function createGQLQueryType (buildToken: BuildToken): GraphQLObjectType<mixed> {
.map(collection => createCollectionQueryFieldEntries(buildToken, collection))
.reduce((a, b) => a.concat(b), []),
[
// The root query type is useful for Relay 1 as it limits what fields
// can be queried at the top level.
['query', {
description: '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.',
type: new GraphQLNonNull(queryType),
resolve: source => source || {},
resolve: source => $$isQuery,
}],
// The root query type needs to implement `Node` and have an id for
// Relay 1 mutations. This may be deprecated in the future.
[options.nodeIdFieldName, {
description: 'The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`.',
type: new GraphQLNonNull(GraphQLID),
resolve: () => 'query',
}],
],
),
Expand Down
9 changes: 8 additions & 1 deletion src/graphql/schema/node/createNodeFieldEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { GraphQLFieldConfig, GraphQLNonNull, GraphQLID } from 'graphql'
import { Collection, ObjectType } from '../../../interface'
import { idSerde, scrib } from '../../utils'
import BuildToken from '../BuildToken'
import { $$isQuery } from '../getQueryGQLType'
import getNodeInterfaceType from './getNodeInterfaceType'

// TODO: doc
Expand All @@ -16,13 +17,19 @@ export default function createNodeFieldEntry (buildToken: BuildToken): [string,
type: new GraphQLNonNull(GraphQLID),
},
},
async resolve (source: mixed, args: { [key: string]: mixed }, context: mixed): Promise<ObjectType.Value | null> {
async resolve (source: mixed, args: { [key: string]: mixed }, context: mixed): Promise<ObjectType.Value | Symbol | null> {
let deserializationResult: { collection: Collection, keyValue: mixed }
const idString = args[options.nodeIdFieldName]

if (typeof idString !== 'string')
throw new Error('ID argument must be a string.')

// If the id is simply `query`, we want to give back our root query type.
// For now this is needed for Relay 1 mutations, maybe deprecate this in
// the future?
if (idString === 'query')
return $$isQuery

// Try to deserialize the id we got from our argument. If we fail to
// deserialize the id, we should just return null and ignore the error.
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1505,13 +1505,38 @@ Object {
exports[`test operation query.graphql 1`] = `
Object {
"data": Object {
"node": null,
"__id": "query",
"a": null,
"b": Object {
"__id": "query",
"__typename": "Query",
"a": null,
"b": Object {
"__id": "query",
"__typename": "Query",
},
},
"query": Object {
"node": null,
"__id": "query",
"a": null,
"b": Object {
"__id": "query",
"__typename": "Query",
},
"query": Object {
"node": null,
"__id": "query",
"a": null,
"b": Object {
"__id": "query",
"__typename": "Query",
},
"query": Object {
"node": null,
"__id": "query",
"a": null,
"b": Object {
"__id": "query",
"__typename": "Query",
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2337,7 +2337,7 @@ input PgTypeInput {
}

# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -4237,6 +4237,9 @@ type Query {
# 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\`.
__id: ID!
}

type ReferentialConstraint {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11745,7 +11745,7 @@ type PlpgsqlValidatorPayload {
}

# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -20124,6 +20124,9 @@ type Query {
# 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\`.
__id: ID!
}

input RandomInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ type Post {
}
# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -538,6 +538,9 @@ type Query {
# 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\`.
id: ID!
}
input TableMutationInput {
Expand Down Expand Up @@ -956,7 +959,7 @@ type PageInfo {
}
# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -1037,6 +1040,9 @@ type Query {
# 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\`.
__id: ID!
}
# The exact time of day, does not include the date. May or may not have a timezone offset.
Expand Down Expand Up @@ -2288,7 +2294,7 @@ enum PostsOrderBy {
}
# The root query type which gives access points into the data universe.
type Query {
type Query implements Node {
# Fetches an object given its globally unique \`ID\`.
node(
# The globally unique \`ID\`.
Expand Down Expand Up @@ -2552,6 +2558,9 @@ type Query {
# 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\`.
__id: ID!
}
input TableMutationInput {
Expand Down
17 changes: 16 additions & 1 deletion src/postgraphql/__tests__/fixtures/queries/query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,25 @@ query {
}
}
}
a: node(__id: "hello") {
__typename
__id
...query
}
b: node(__id: "query") {
__typename
__id
...query
}
}

fragment query on Query {
node(__id: "hello") {
__id
a: node(__id: "hello") {
__typename
__id
}
b: node(__id: "query") {
__typename
__id
}
Expand Down

0 comments on commit 594b554

Please sign in to comment.