feat: add onObjectDefinition / onInputObjectDefinition (#533)
This commit is contained in:
parent
0dd8ea367b
commit
b4e0debd95
|
@ -20,6 +20,7 @@ jobs:
|
||||||
run: yarn -s test:ci
|
run: yarn -s test:ci
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
|
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '10.x'
|
||||||
with:
|
with:
|
||||||
directory: ./coverage
|
directory: ./coverage
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ jobs:
|
||||||
- name: Test
|
- name: Test
|
||||||
run: yarn -s test:ci
|
run: yarn -s test:ci
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
|
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '10.x'
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
with:
|
with:
|
||||||
directory: ./coverage
|
directory: ./coverage
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
codecov:
|
||||||
|
require_ci_to_pass: no
|
||||||
|
notify:
|
||||||
|
wait_for_ci: no
|
||||||
|
|
||||||
comment:
|
comment:
|
||||||
layout: "diff"
|
layout: 'diff'
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
status:
|
status:
|
||||||
|
|
|
@ -78,6 +78,49 @@ plugin({
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### onObjectDefinition(t, objectConfig)
|
||||||
|
|
||||||
|
The "onObjectDefinition" hook is called when an `objectType` is created, and is provided `t`, the object
|
||||||
|
passed into the `definition` block, as well as the `config` of the object.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const NodePlugin = plugin({
|
||||||
|
name: 'NodePlugin',
|
||||||
|
description: 'Allows us to designate the field used to determine the "node" interface',
|
||||||
|
objectTypeDefTypes: `node?: string | core.FieldResolver<TypeName, any>`,
|
||||||
|
onObjectDefinition(t, { node }) {
|
||||||
|
if (node) {
|
||||||
|
let resolveFn
|
||||||
|
if (typeof node === 'string') {
|
||||||
|
const fieldResolve: FieldResolver<any, any> = (root, args, ctx, info) => {
|
||||||
|
return `${info.parentType.name}:${root[node]}`
|
||||||
|
}
|
||||||
|
resolveFn = fieldResolve
|
||||||
|
} else {
|
||||||
|
resolveFn = node
|
||||||
|
}
|
||||||
|
t.implements('Node')
|
||||||
|
t.id('id', {
|
||||||
|
nullable: false,
|
||||||
|
resolve: resolveFn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const User = objectType({
|
||||||
|
name: 'User',
|
||||||
|
node: 'id', // adds `id` field
|
||||||
|
definition(t) {
|
||||||
|
t.string('name')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### onCreateFieldResolver(config)
|
### onCreateFieldResolver(config)
|
||||||
|
|
||||||
Every ObjectType, whether they are defined via Nexus' `objectType` api, or elsewhere is given a resolver.
|
Every ObjectType, whether they are defined via Nexus' `objectType` api, or elsewhere is given a resolver.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { plugin } from '@nexus/schema'
|
import { plugin, interfaceType, FieldResolver } from '@nexus/schema'
|
||||||
|
|
||||||
export const logMutationTimePlugin = plugin({
|
export const logMutationTimePlugin = plugin({
|
||||||
name: 'LogMutationTime',
|
name: 'LogMutationTime',
|
||||||
|
@ -15,3 +15,50 @@ export const logMutationTimePlugin = plugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const NodePlugin = plugin({
|
||||||
|
name: 'NodePlugin',
|
||||||
|
description: 'Allows us to designate the field used to ',
|
||||||
|
objectTypeDefTypes: `node?: string | core.FieldResolver<TypeName, any>`,
|
||||||
|
onObjectDefinition(t, { node }) {
|
||||||
|
if (node) {
|
||||||
|
let resolveFn
|
||||||
|
if (typeof node === 'string') {
|
||||||
|
const fieldResolve: FieldResolver<any, any> = (root, args, ctx, info) => {
|
||||||
|
return `${info.parentType.name}:${root[node]}`
|
||||||
|
}
|
||||||
|
resolveFn = fieldResolve
|
||||||
|
} else {
|
||||||
|
resolveFn = node
|
||||||
|
}
|
||||||
|
t.implements('Node')
|
||||||
|
t.id('id', {
|
||||||
|
nullable: false,
|
||||||
|
resolve: resolveFn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMissingType(t, builder) {
|
||||||
|
if (t === 'Node') {
|
||||||
|
return interfaceType({
|
||||||
|
name: 'Node',
|
||||||
|
description:
|
||||||
|
'A "Node" is a field with a required ID field (id), per the https://relay.dev/docs/en/graphql-server-specification',
|
||||||
|
definition(t) {
|
||||||
|
t.id('id', {
|
||||||
|
nullable: false,
|
||||||
|
resolve: () => {
|
||||||
|
throw new Error('Abstract')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
t.resolveType((t) => {
|
||||||
|
if (t.__typename) {
|
||||||
|
return t.__typename
|
||||||
|
}
|
||||||
|
throw new Error('__typename missing for resolving Node')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { ApolloServer } from 'apollo-server'
|
||||||
import { separateOperations } from 'graphql'
|
import { separateOperations } from 'graphql'
|
||||||
import { fieldExtensionsEstimator, getComplexity, simpleEstimator } from 'graphql-query-complexity'
|
import { fieldExtensionsEstimator, getComplexity, simpleEstimator } from 'graphql-query-complexity'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { logMutationTimePlugin } from './example-plugins'
|
import { logMutationTimePlugin, NodePlugin } from './example-plugins'
|
||||||
import * as types from './kitchen-sink-definitions'
|
import * as types from './kitchen-sink-definitions'
|
||||||
|
|
||||||
const DEBUGGING_CURSOR = false
|
const DEBUGGING_CURSOR = false
|
||||||
|
@ -24,6 +24,7 @@ const schema = makeSchema({
|
||||||
typegen: path.join(__dirname, './kitchen-sink.gen.ts'),
|
typegen: path.join(__dirname, './kitchen-sink.gen.ts'),
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
NodePlugin,
|
||||||
connectionPlugin({
|
connectionPlugin({
|
||||||
encodeCursor: fn,
|
encodeCursor: fn,
|
||||||
decodeCursor: fn,
|
decodeCursor: fn,
|
||||||
|
|
|
@ -31,18 +31,6 @@ export const testArgs2 = {
|
||||||
bar: idArg(),
|
bar: idArg(),
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Node = interfaceType({
|
|
||||||
name: 'Node',
|
|
||||||
definition(t) {
|
|
||||||
t.id('id', {
|
|
||||||
nullable: false,
|
|
||||||
resolve: () => {
|
|
||||||
throw new Error('Abstract')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Mutation = mutationType({
|
export const Mutation = mutationType({
|
||||||
definition(t) {
|
definition(t) {
|
||||||
t.boolean('ok', () => true)
|
t.boolean('ok', () => true)
|
||||||
|
@ -118,6 +106,7 @@ export const TestUnion = unionType({
|
||||||
|
|
||||||
export const TestObj = objectType({
|
export const TestObj = objectType({
|
||||||
name: 'TestObj',
|
name: 'TestObj',
|
||||||
|
node: (obj) => `TestObj:${obj.item}`,
|
||||||
definition(t) {
|
definition(t) {
|
||||||
t.implements('Bar', Baz)
|
t.implements('Bar', Baz)
|
||||||
t.string('item')
|
t.string('item')
|
||||||
|
|
|
@ -613,6 +613,7 @@ enum NodeFlags {
|
||||||
Namespace
|
Namespace
|
||||||
NestedNamespace
|
NestedNamespace
|
||||||
None
|
None
|
||||||
|
OptionalChain
|
||||||
PermanentlySetIncrementalFlags
|
PermanentlySetIncrementalFlags
|
||||||
PossiblyContainsDynamicImport
|
PossiblyContainsDynamicImport
|
||||||
PossiblyContainsImportMeta
|
PossiblyContainsImportMeta
|
||||||
|
@ -621,6 +622,7 @@ enum NodeFlags {
|
||||||
Synthesized
|
Synthesized
|
||||||
ThisNodeHasError
|
ThisNodeHasError
|
||||||
ThisNodeOrAnySubNodesHasError
|
ThisNodeOrAnySubNodesHasError
|
||||||
|
TypeCached
|
||||||
TypeExcludesFlags
|
TypeExcludesFlags
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
YieldContext
|
YieldContext
|
||||||
|
@ -842,6 +844,7 @@ enum SyntaxKind {
|
||||||
ArrowFunction
|
ArrowFunction
|
||||||
AsExpression
|
AsExpression
|
||||||
AsKeyword
|
AsKeyword
|
||||||
|
AssertsKeyword
|
||||||
AsteriskAsteriskEqualsToken
|
AsteriskAsteriskEqualsToken
|
||||||
AsteriskAsteriskToken
|
AsteriskAsteriskToken
|
||||||
AsteriskEqualsToken
|
AsteriskEqualsToken
|
||||||
|
@ -942,6 +945,7 @@ enum SyntaxKind {
|
||||||
FirstNode
|
FirstNode
|
||||||
FirstPunctuation
|
FirstPunctuation
|
||||||
FirstReservedWord
|
FirstReservedWord
|
||||||
|
FirstStatement
|
||||||
FirstTemplateToken
|
FirstTemplateToken
|
||||||
FirstToken
|
FirstToken
|
||||||
FirstTriviaToken
|
FirstTriviaToken
|
||||||
|
@ -994,12 +998,17 @@ enum SyntaxKind {
|
||||||
JSDocComment
|
JSDocComment
|
||||||
JSDocEnumTag
|
JSDocEnumTag
|
||||||
JSDocFunctionType
|
JSDocFunctionType
|
||||||
|
JSDocImplementsTag
|
||||||
JSDocNamepathType
|
JSDocNamepathType
|
||||||
JSDocNonNullableType
|
JSDocNonNullableType
|
||||||
JSDocNullableType
|
JSDocNullableType
|
||||||
JSDocOptionalType
|
JSDocOptionalType
|
||||||
JSDocParameterTag
|
JSDocParameterTag
|
||||||
|
JSDocPrivateTag
|
||||||
JSDocPropertyTag
|
JSDocPropertyTag
|
||||||
|
JSDocProtectedTag
|
||||||
|
JSDocPublicTag
|
||||||
|
JSDocReadonlyTag
|
||||||
JSDocReturnTag
|
JSDocReturnTag
|
||||||
JSDocSignature
|
JSDocSignature
|
||||||
JSDocTag
|
JSDocTag
|
||||||
|
@ -1037,6 +1046,7 @@ enum SyntaxKind {
|
||||||
LastLiteralToken
|
LastLiteralToken
|
||||||
LastPunctuation
|
LastPunctuation
|
||||||
LastReservedWord
|
LastReservedWord
|
||||||
|
LastStatement
|
||||||
LastTemplateToken
|
LastTemplateToken
|
||||||
LastToken
|
LastToken
|
||||||
LastTriviaToken
|
LastTriviaToken
|
||||||
|
@ -1063,6 +1073,7 @@ enum SyntaxKind {
|
||||||
MultiLineCommentTrivia
|
MultiLineCommentTrivia
|
||||||
NamedExports
|
NamedExports
|
||||||
NamedImports
|
NamedImports
|
||||||
|
NamespaceExport
|
||||||
NamespaceExportDeclaration
|
NamespaceExportDeclaration
|
||||||
NamespaceImport
|
NamespaceImport
|
||||||
NamespaceKeyword
|
NamespaceKeyword
|
||||||
|
@ -1097,6 +1108,7 @@ enum SyntaxKind {
|
||||||
PlusToken
|
PlusToken
|
||||||
PostfixUnaryExpression
|
PostfixUnaryExpression
|
||||||
PrefixUnaryExpression
|
PrefixUnaryExpression
|
||||||
|
PrivateIdentifier
|
||||||
PrivateKeyword
|
PrivateKeyword
|
||||||
PropertyAccessExpression
|
PropertyAccessExpression
|
||||||
PropertyAssignment
|
PropertyAssignment
|
||||||
|
@ -1105,6 +1117,8 @@ enum SyntaxKind {
|
||||||
ProtectedKeyword
|
ProtectedKeyword
|
||||||
PublicKeyword
|
PublicKeyword
|
||||||
QualifiedName
|
QualifiedName
|
||||||
|
QuestionDotToken
|
||||||
|
QuestionQuestionToken
|
||||||
QuestionToken
|
QuestionToken
|
||||||
ReadonlyKeyword
|
ReadonlyKeyword
|
||||||
RegularExpressionLiteral
|
RegularExpressionLiteral
|
||||||
|
@ -1133,6 +1147,7 @@ enum SyntaxKind {
|
||||||
SymbolKeyword
|
SymbolKeyword
|
||||||
SyntaxList
|
SyntaxList
|
||||||
SyntheticExpression
|
SyntheticExpression
|
||||||
|
SyntheticReferenceExpression
|
||||||
TaggedTemplateExpression
|
TaggedTemplateExpression
|
||||||
TemplateExpression
|
TemplateExpression
|
||||||
TemplateHead
|
TemplateHead
|
||||||
|
|
|
@ -403,6 +403,16 @@ export class SchemaBuilder {
|
||||||
*/
|
*/
|
||||||
protected onAfterBuildFns: Exclude<PluginConfig['onAfterBuild'], undefined>[] = []
|
protected onAfterBuildFns: Exclude<PluginConfig['onAfterBuild'], undefined>[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed after the object is defined, allowing us to add additional fields to the object
|
||||||
|
*/
|
||||||
|
protected onObjectDefinitionFns: Exclude<PluginConfig['onObjectDefinition'], undefined>[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed after the object is defined, allowing us to add additional fields to the object
|
||||||
|
*/
|
||||||
|
protected onInputObjectDefinitionFns: Exclude<PluginConfig['onInputObjectDefinition'], undefined>[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `schemaExtension` is created just after the types are walked,
|
* The `schemaExtension` is created just after the types are walked,
|
||||||
* but before the fields are materialized.
|
* but before the fields are materialized.
|
||||||
|
@ -673,6 +683,12 @@ export class SchemaBuilder {
|
||||||
if (pluginConfig.onAfterBuild) {
|
if (pluginConfig.onAfterBuild) {
|
||||||
this.onAfterBuildFns.push(pluginConfig.onAfterBuild)
|
this.onAfterBuildFns.push(pluginConfig.onAfterBuild)
|
||||||
}
|
}
|
||||||
|
if (pluginConfig.onObjectDefinition) {
|
||||||
|
this.onObjectDefinitionFns.push(pluginConfig.onObjectDefinition)
|
||||||
|
}
|
||||||
|
if (pluginConfig.onInputObjectDefinition) {
|
||||||
|
this.onInputObjectDefinitionFns.push(pluginConfig.onInputObjectDefinition)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,6 +777,9 @@ export class SchemaBuilder {
|
||||||
warn: consoleWarn,
|
warn: consoleWarn,
|
||||||
})
|
})
|
||||||
config.definition(definitionBlock)
|
config.definition(definitionBlock)
|
||||||
|
this.onInputObjectDefinitionFns.forEach((fn) => {
|
||||||
|
fn(definitionBlock, config)
|
||||||
|
})
|
||||||
const extensions = this.inputTypeExtendMap[config.name]
|
const extensions = this.inputTypeExtendMap[config.name]
|
||||||
if (extensions) {
|
if (extensions) {
|
||||||
extensions.forEach((extension) => {
|
extensions.forEach((extension) => {
|
||||||
|
@ -790,6 +809,9 @@ export class SchemaBuilder {
|
||||||
warn: consoleWarn,
|
warn: consoleWarn,
|
||||||
})
|
})
|
||||||
config.definition(definitionBlock)
|
config.definition(definitionBlock)
|
||||||
|
this.onObjectDefinitionFns.forEach((fn) => {
|
||||||
|
fn(definitionBlock, config)
|
||||||
|
})
|
||||||
const extensions = this.typeExtendMap[config.name]
|
const extensions = this.typeExtendMap[config.name]
|
||||||
if (extensions) {
|
if (extensions) {
|
||||||
extensions.forEach((extension) => {
|
extensions.forEach((extension) => {
|
||||||
|
|
|
@ -11,6 +11,9 @@ import {
|
||||||
} from './definitions/_types'
|
} from './definitions/_types'
|
||||||
import { NexusSchemaExtension } from './extensions'
|
import { NexusSchemaExtension } from './extensions'
|
||||||
import { isPromiseLike, PrintedGenTyping, PrintedGenTypingImport, venn } from './utils'
|
import { isPromiseLike, PrintedGenTyping, PrintedGenTypingImport, venn } from './utils'
|
||||||
|
import { NexusObjectTypeConfig, ObjectDefinitionBlock } from './definitions/objectType'
|
||||||
|
import { InputDefinitionBlock } from './definitions/definitionBlocks'
|
||||||
|
import { NexusInputObjectTypeConfig } from './definitions/inputObjectType'
|
||||||
|
|
||||||
export { PluginBuilderLens }
|
export { PluginBuilderLens }
|
||||||
|
|
||||||
|
@ -89,6 +92,19 @@ export interface PluginConfig {
|
||||||
* After the schema is built, provided the Schema to do any final config validation.
|
* After the schema is built, provided the Schema to do any final config validation.
|
||||||
*/
|
*/
|
||||||
onAfterBuild?: (schema: GraphQLSchema) => void
|
onAfterBuild?: (schema: GraphQLSchema) => void
|
||||||
|
/**
|
||||||
|
* Called immediately after the object is defined, allows for using metadata
|
||||||
|
* to define the shape of the object.
|
||||||
|
*/
|
||||||
|
onObjectDefinition?: (block: ObjectDefinitionBlock<any>, objectConfig: NexusObjectTypeConfig<any>) => void
|
||||||
|
/**
|
||||||
|
* Called immediately after the input object is defined, allows for using metadata
|
||||||
|
* to define the shape of the input object
|
||||||
|
*/
|
||||||
|
onInputObjectDefinition?: (
|
||||||
|
block: InputDefinitionBlock<any>,
|
||||||
|
objectConfig: NexusInputObjectTypeConfig<any>
|
||||||
|
) => void
|
||||||
/**
|
/**
|
||||||
* If a type is not defined in the schema, our plugins can register an `onMissingType` handler,
|
* If a type is not defined in the schema, our plugins can register an `onMissingType` handler,
|
||||||
* which will intercept the missing type name and give us an opportunity to respond with a valid
|
* which will intercept the missing type name and give us an opportunity to respond with a valid
|
||||||
|
@ -206,6 +222,8 @@ function validatePluginConfig(pluginConfig: PluginConfig): void {
|
||||||
'onBeforeBuild',
|
'onBeforeBuild',
|
||||||
'onMissingType',
|
'onMissingType',
|
||||||
'onAfterBuild',
|
'onAfterBuild',
|
||||||
|
'onObjectDefinition',
|
||||||
|
'onInputObjectDefinition',
|
||||||
]
|
]
|
||||||
const validOptionalProps = ['description', 'fieldDefTypes', 'objectTypeDefTypes', ...optionalPropFns]
|
const validOptionalProps = ['description', 'fieldDefTypes', 'objectTypeDefTypes', ...optionalPropFns]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { buildSchema, graphql, GraphQLSchema, printSchema } from 'graphql'
|
import { buildSchema, graphql, GraphQLSchema, printSchema, introspectionFromSchema } from 'graphql'
|
||||||
import { makeSchema, MiddlewareFn, objectType, plugin, queryField } from '../src/core'
|
import { makeSchema, MiddlewareFn, objectType, plugin, queryField, interfaceType } from '../src/core'
|
||||||
import { nullabilityGuardPlugin } from '../src/plugins'
|
import { nullabilityGuardPlugin } from '../src/plugins'
|
||||||
import { EXAMPLE_SDL } from './_sdl'
|
import { EXAMPLE_SDL } from './_sdl'
|
||||||
|
|
||||||
|
@ -203,6 +203,70 @@ describe('plugin', () => {
|
||||||
)
|
)
|
||||||
expect(calls).toMatchSnapshot()
|
expect(calls).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has an onObjectDefinition option, which receives the object metadata', async () => {
|
||||||
|
//
|
||||||
|
const schema = makeSchema({
|
||||||
|
outputs: false,
|
||||||
|
types: [
|
||||||
|
interfaceType({
|
||||||
|
name: 'Node',
|
||||||
|
definition(t) {
|
||||||
|
t.id('id', {
|
||||||
|
nullable: false,
|
||||||
|
resolve: () => {
|
||||||
|
throw new Error('Abstract')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
t.resolveType((n) => n.__typename)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
objectType({
|
||||||
|
name: 'AddsNode',
|
||||||
|
// @ts-ignore
|
||||||
|
node: 'id',
|
||||||
|
definition(t) {
|
||||||
|
t.string('name')
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
queryField('getNode', {
|
||||||
|
type: 'Node',
|
||||||
|
resolve: () => ({ __typename: 'AddsNode', name: 'test', id: 'abc' }),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
plugin({
|
||||||
|
name: 'Node',
|
||||||
|
onObjectDefinition(t, config) {
|
||||||
|
const node = (config as any).node as any
|
||||||
|
if (node) {
|
||||||
|
t.implements('Node')
|
||||||
|
t.id('id', {
|
||||||
|
nullable: false,
|
||||||
|
resolve: (root) => `${config.name}:${root[node]}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const result = await graphql(
|
||||||
|
schema,
|
||||||
|
`
|
||||||
|
{
|
||||||
|
getNode {
|
||||||
|
__typename
|
||||||
|
id
|
||||||
|
... on AddsNode {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
expect(result.data?.getNode).toEqual({ __typename: 'AddsNode', id: 'AddsNode:abc', name: 'test' })
|
||||||
|
})
|
||||||
|
|
||||||
it('has a plugin.completeValue fn which is used to efficiently complete a value which is possibly a promise', async () => {
|
it('has a plugin.completeValue fn which is used to efficiently complete a value which is possibly a promise', async () => {
|
||||||
const calls: string[] = []
|
const calls: string[] = []
|
||||||
const testResolve = (name: string) => (): MiddlewareFn => async (root, args, ctx, info, next) => {
|
const testResolve = (name: string) => (): MiddlewareFn => async (root, args, ctx, info, next) => {
|
||||||
|
|
Loading…
Reference in New Issue