Skip to content

Commit

Permalink
feat: forward nexus plugins into t.model and t.crud (graphql-nexus#687)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky committed Jun 5, 2020
1 parent 7a21a04 commit 4b64359
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 151 deletions.
11 changes: 11 additions & 0 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,18 @@ export class SchemaBuilder {
buildFieldConfig(
config: FieldConfigData,
): Nexus.core.NexusOutputFieldConfig<any, string> {
const {
alias,
locallyComputedInputs,
type,
filtering,
ordering,
pagination,
...additionalExternalPropsSuchAsPlugins
} = config.publisherConfig

return {
...additionalExternalPropsSuchAsPlugins,
type: this.publisher.outputType(
config.publisherConfig.type,
config.field,
Expand Down
26 changes: 13 additions & 13 deletions src/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ function renderStaticTypes() {
? true
: false
type NexusPrismaScalarOpts = {
alias?: string
}
type NexusPrismaScalarOpts<TypeName extends string, MethodName extends string, Alias extends string | undefined> = {
alias?: Alias
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>
type Pagination = {
skip?: boolean
Expand Down Expand Up @@ -332,21 +332,21 @@ function renderStaticTypes() {
export type Context = core.GetGen<'context'>
type BaseRelationOptions<MethodName extends string, ReturnType extends string> =
type BaseRelationOptions<TypeName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
DynamicRequiredType<ReturnType> & {
alias?: string
alias?: Alias
computedInputs?: LocalComputedInputs<MethodName>
}
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>
// If GetNexusPrismaInput returns never, it means there are no filtering/ordering args for it.
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, ReturnType extends string> =
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
GetNexusPrismaInput<ModelName, MethodName, 'filtering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else if
: GetNexusPrismaInput<ModelName, MethodName, 'ordering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else
: BaseRelationOptions<MethodName, ReturnType> & {
: BaseRelationOptions<ModelName, MethodName, Alias, ReturnType> & {
filtering?:
| boolean
| Partial<Record<GetNexusPrismaInput<ModelName, MethodName, 'filtering'>, boolean>>
Expand Down Expand Up @@ -431,14 +431,14 @@ function renderStaticTypes() {
// else if
// if scalar return scalar opts
: ThisKind extends AKind<'Scalar'>
? (opts?: NexusPrismaScalarOpts) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaScalarOpts<ModelName, MethodName, Alias>) => NexusPrismaFields<ModelName>
// else if
// if model name has a mapped graphql types then make opts optional
: IsModelNameExistsInGraphQLTypes<ReturnType> extends true
? (opts?: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>
// else
// force use input the related graphql type -> { type: '...' }
: (opts: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
: <Alias extends string | undefined = undefined>(opts: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>
type GetNexusPrismaMethod<TypeName extends string> = TypeName extends keyof NexusPrismaMethods
? NexusPrismaMethods[TypeName]
Expand Down
26 changes: 13 additions & 13 deletions tests/__app/generated/nexus-prisma-typegen.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ type IsModelNameExistsInGraphQLTypes<ReturnType> =
? true
: false

type NexusPrismaScalarOpts = {
alias?: string
}
type NexusPrismaScalarOpts<TypeName extends string, MethodName extends string, Alias extends string | undefined> = {
alias?: Alias
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>

type Pagination = {
skip?: boolean
Expand Down Expand Up @@ -109,21 +109,21 @@ export type LocalMutationResolverParams<MethodName extends string> =

export type Context = core.GetGen<'context'>

type BaseRelationOptions<MethodName extends string, ReturnType extends string> =
type BaseRelationOptions<TypeName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
DynamicRequiredType<ReturnType> & {
alias?: string
alias?: Alias
computedInputs?: LocalComputedInputs<MethodName>
}
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>

// If GetNexusPrismaInput returns never, it means there are no filtering/ordering args for it.
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, ReturnType extends string> =
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
GetNexusPrismaInput<ModelName, MethodName, 'filtering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else if
: GetNexusPrismaInput<ModelName, MethodName, 'ordering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else
: BaseRelationOptions<MethodName, ReturnType> & {
: BaseRelationOptions<ModelName, MethodName, Alias, ReturnType> & {
filtering?:
| boolean
| Partial<Record<GetNexusPrismaInput<ModelName, MethodName, 'filtering'>, boolean>>
Expand Down Expand Up @@ -208,14 +208,14 @@ type NexusPrismaMethod<
// else if
// if scalar return scalar opts
: ThisKind extends AKind<'Scalar'>
? (opts?: NexusPrismaScalarOpts) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaScalarOpts<ModelName, MethodName, Alias>) => NexusPrismaFields<ModelName>
// else if
// if model name has a mapped graphql types then make opts optional
: IsModelNameExistsInGraphQLTypes<ReturnType> extends true
? (opts?: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>
// else
// force use input the related graphql type -> { type: '...' }
: (opts: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
: <Alias extends string | undefined = undefined>(opts: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>

type GetNexusPrismaMethod<TypeName extends string> = TypeName extends keyof NexusPrismaMethods
? NexusPrismaMethods[TypeName]
Expand Down
14 changes: 8 additions & 6 deletions tests/__client-test-context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Nexus from '@nexus/schema'
import { MigrateEngine } from '@prisma/migrate'
import { getGenerator } from '@prisma/sdk'
import * as fs from 'fs'
Expand All @@ -15,6 +16,7 @@ type RuntimeTestContext = {
getContext: (args: {
datamodel: string
types: any[]
plugins?: Nexus.core.NexusPlugin[]
}) => Promise<{
graphqlClient: GraphQLClient
dbClient: any
Expand Down Expand Up @@ -43,7 +45,7 @@ export function createRuntimeTestContext(): RuntimeTestContext {
afterEach(teardownCtx)

return {
async getContext({ datamodel, types }) {
async getContext({ datamodel, types, plugins }) {
try {
// Force query engine binary path
process.env.PRISMA_QUERY_ENGINE_BINARY = await getEnginePath('query')
Expand All @@ -54,6 +56,7 @@ export function createRuntimeTestContext(): RuntimeTestContext {
const serverAndClient = await getGraphQLServerAndClient(
datamodel,
types,
plugins ?? [],
prismaClient,
)
httpServer = serverAndClient.httpServer
Expand All @@ -75,13 +78,12 @@ export function createRuntimeTestContext(): RuntimeTestContext {
async function getGraphQLServerAndClient(
datamodel: string,
types: any[],
plugins: Nexus.core.NexusPlugin[],
prismaClient: { client: any; teardown(): Promise<void> },
) {
const { schema, schemaString } = await generateSchemaAndTypes(
datamodel,
types,
{},
)
const { schema, schemaString } = await generateSchemaAndTypes(datamodel, types, {
plugins,
})
const port = await getPort()
const endpoint = '/graphql'
const graphqlServer = new GraphQLServer({
Expand Down
26 changes: 13 additions & 13 deletions tests/__snapshots__/app.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -565,9 +565,9 @@ type IsModelNameExistsInGraphQLTypes<ReturnType> =
? true
: false

type NexusPrismaScalarOpts = {
alias?: string
}
type NexusPrismaScalarOpts<TypeName extends string, MethodName extends string, Alias extends string | undefined> = {
alias?: Alias
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>

type Pagination = {
skip?: boolean
Expand Down Expand Up @@ -666,21 +666,21 @@ export type LocalMutationResolverParams<MethodName extends string> =

export type Context = core.GetGen<'context'>

type BaseRelationOptions<MethodName extends string, ReturnType extends string> =
type BaseRelationOptions<TypeName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
DynamicRequiredType<ReturnType> & {
alias?: string
alias?: Alias
computedInputs?: LocalComputedInputs<MethodName>
}
} & NexusGenPluginFieldConfig<TypeName, Alias extends undefined ? MethodName : Alias>

// If GetNexusPrismaInput returns never, it means there are no filtering/ordering args for it.
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, ReturnType extends string> =
type NexusPrismaRelationOpts<ModelName extends string, MethodName extends string, Alias extends string | undefined, ReturnType extends string> =
GetNexusPrismaInput<ModelName, MethodName, 'filtering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else if
: GetNexusPrismaInput<ModelName, MethodName, 'ordering'> extends never
? BaseRelationOptions<MethodName, ReturnType>
? BaseRelationOptions<ModelName, MethodName, Alias, ReturnType>
// else
: BaseRelationOptions<MethodName, ReturnType> & {
: BaseRelationOptions<ModelName, MethodName, Alias, ReturnType> & {
filtering?:
| boolean
| Partial<Record<GetNexusPrismaInput<ModelName, MethodName, 'filtering'>, boolean>>
Expand Down Expand Up @@ -765,14 +765,14 @@ type NexusPrismaMethod<
// else if
// if scalar return scalar opts
: ThisKind extends AKind<'Scalar'>
? (opts?: NexusPrismaScalarOpts) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaScalarOpts<ModelName, MethodName, Alias>) => NexusPrismaFields<ModelName>
// else if
// if model name has a mapped graphql types then make opts optional
: IsModelNameExistsInGraphQLTypes<ReturnType> extends true
? (opts?: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
? <Alias extends string | undefined = undefined>(opts?: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>
// else
// force use input the related graphql type -> { type: '...' }
: (opts: NexusPrismaRelationOpts<ModelName, MethodName, ReturnType>) => NexusPrismaFields<ModelName>
: <Alias extends string | undefined = undefined>(opts: NexusPrismaRelationOpts<ModelName, MethodName, Alias, ReturnType>) => NexusPrismaFields<ModelName>

type GetNexusPrismaMethod<TypeName extends string> = TypeName extends keyof NexusPrismaMethods
? NexusPrismaMethods[TypeName]
Expand Down
7 changes: 5 additions & 2 deletions tests/__utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ export async function getPinnedDmmfFromSchema(datamodel: string) {
export async function generateSchemaAndTypes(
datamodel: string,
types: any[],
options?: TransformOptions & { experimentalCRUD?: boolean },
options?: TransformOptions & {
experimentalCRUD?: boolean
plugins?: Nexus.core.NexusPlugin[]
},
) {
const dmmf = await getDmmf(datamodel, options)
const nexusPrisma = createNexusPrismaInternal({
Expand All @@ -57,7 +60,7 @@ export async function generateSchemaAndTypes(
})
const schema = Nexus.makeSchema({
types,
plugins: [nexusPrisma],
plugins: [nexusPrisma, ...(options?.plugins ?? [])],
outputs: false,
})

Expand Down
5 changes: 5 additions & 0 deletions tests/runtime/__snapshots__/plugins.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`forwards plugins to t.crud 1`] = `[Error: Not authorized: {"response":{"data":null,"errors":[{"message":"Not authorized","locations":[{"line":2,"column":7}],"path":["users"]}],"status":200},"request":{"query":"{\\n users {\\n id\\n }\\n }"}}]`;

exports[`forwards plugins to t.model 1`] = `[Error: Not authorized: {"response":{"data":null,"errors":[{"message":"Not authorized","locations":[{"line":3,"column":9}],"path":["users",0,"id"]}],"status":200},"request":{"query":"{\\n users {\\n id\\n }\\n }"}}]`;
100 changes: 100 additions & 0 deletions tests/runtime/plugins.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { objectType, fieldAuthorizePlugin } from '@nexus/schema'
import { createRuntimeTestContext } from '../__client-test-context'

let ctx = createRuntimeTestContext()

it('forwards plugins to t.model', async () => {
const datamodel = `
model User {
id Int @id @default(autoincrement())
name String
}
`
const User = objectType({
name: 'User',
definition(t: any) {
t.model.id({
authorize() {
return new Error('nope')
},
})
},
})

const Query = objectType({
name: 'Query',
definition(t: any) {
t.crud.users()
},
})

const { graphqlClient, dbClient } = await ctx.getContext({
datamodel,
types: [Query, User],
plugins: [fieldAuthorizePlugin()],
})

await dbClient.user.create({
data: {
name: 'Foo',
},
})

try {
await graphqlClient.request(`{
users {
id
}
}`)
} catch (e) {
expect(e).toMatchSnapshot()
}
})

it('forwards plugins to t.crud', async () => {
const datamodel = `
model User {
id Int @id @default(autoincrement())
name String
}
`
const User = objectType({
name: 'User',
definition(t: any) {
t.model.id()
},
})

const Query = objectType({
name: 'Query',
definition(t: any) {
t.crud.users({
authorize() {
return new Error('nope')
},
})
},
})

const { graphqlClient, dbClient } = await ctx.getContext({
datamodel,
types: [Query, User],
plugins: [fieldAuthorizePlugin()],
})

await dbClient.user.create({
data: {
name: 'Foo',
},
})

try {
await graphqlClient.request(`{
users {
id
}
}`)
} catch (e) {
expect(e).toMatchSnapshot()
}
})
Loading

0 comments on commit 4b64359

Please sign in to comment.