176 lines
5.1 KiB
TypeScript
176 lines
5.1 KiB
TypeScript
import { JSONObjectResolver } from 'graphql-scalars'
|
|
import PrismaPlugin, {
|
|
PothosPrismaDatamodel,
|
|
PrismaClient,
|
|
} from '@pothos/plugin-prisma'
|
|
import { Request, Response } from 'express'
|
|
import SmartSubscriptionPlugin, {
|
|
subscribeOptionsFromIterator,
|
|
} from '@pothos/plugin-smart-subscriptions'
|
|
import ZodPlugin from '@pothos/plugin-zod'
|
|
import AuthzPlugin from '@pothos/plugin-authz'
|
|
import ErrorsPlugin from '@pothos/plugin-errors'
|
|
import type { FileUpload } from 'graphql-upload/processRequest.js'
|
|
import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
|
|
import { Injectable, Logger } from '@nestjs/common'
|
|
import { PrismaCrudGenerator } from './graphql.generator'
|
|
import type PrismaTypes from '../types/pothos.generated'
|
|
import PrismaUtils from '@pothos/plugin-prisma-utils'
|
|
import { PubSub } from 'graphql-subscriptions'
|
|
import RelayPlugin from '@pothos/plugin-relay'
|
|
import SchemaBuilder from '@pothos/core'
|
|
import SimpleObjectPlugin from '@pothos/plugin-simple-objects'
|
|
import { User } from '@prisma/client'
|
|
import { getDatamodel } from '../types/pothos.generated'
|
|
import { DateTime } from 'luxon'
|
|
import { Kind } from 'graphql'
|
|
import { DateTimeUtils } from '../common/utils/datetime.utils'
|
|
import { JsonValue } from '@prisma/client/runtime/library'
|
|
|
|
export type SchemaContext =
|
|
| {
|
|
isSubscription: true
|
|
websocket: {
|
|
req: Request
|
|
pubSub: PubSub
|
|
me: User
|
|
generator: PrismaCrudGenerator<BuilderTypes>
|
|
}
|
|
}
|
|
| {
|
|
isSubscription: false
|
|
http: {
|
|
req: Request
|
|
res: Response
|
|
me: User | null
|
|
pubSub: PubSub
|
|
invalidateCache: () => Promise<void>
|
|
generator: PrismaCrudGenerator<BuilderTypes>
|
|
}
|
|
}
|
|
|
|
// extend prisma types to contain string type
|
|
export interface SchemaBuilderOption {
|
|
Context: SchemaContext
|
|
PrismaTypes: PrismaTypes
|
|
DataModel: PothosPrismaDatamodel
|
|
Connection: {
|
|
totalCount: number | (() => number | Promise<number>)
|
|
}
|
|
// AuthZRule: keyof typeof rules;
|
|
Scalars: {
|
|
DateTime: {
|
|
Input: string | DateTime | Date
|
|
Output: string | DateTime | Date
|
|
}
|
|
Json: {
|
|
Input: JsonValue
|
|
Output: JsonValue
|
|
}
|
|
Upload: {
|
|
Input: FileUpload
|
|
Output: FileUpload
|
|
}
|
|
Int: {
|
|
Input: number
|
|
Output: number | bigint | string
|
|
}
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class Builder extends SchemaBuilder<SchemaBuilderOption> {
|
|
public generator: PrismaCrudGenerator<BuilderTypes>
|
|
|
|
constructor(private readonly prisma: PrismaClient) {
|
|
super({
|
|
plugins: [
|
|
PrismaPlugin,
|
|
PrismaUtils,
|
|
SimpleObjectPlugin,
|
|
SmartSubscriptionPlugin,
|
|
RelayPlugin,
|
|
ErrorsPlugin,
|
|
AuthzPlugin,
|
|
ZodPlugin,
|
|
],
|
|
smartSubscriptions: {
|
|
debounceDelay: 1000,
|
|
...subscribeOptionsFromIterator((name, ctx) => {
|
|
if (ctx.isSubscription) {
|
|
return ctx.websocket.pubSub.asyncIterator(name)
|
|
}
|
|
return ctx.http.pubSub.asyncIterator(name)
|
|
}),
|
|
},
|
|
zod: {
|
|
// optionally customize how errors are formatted
|
|
validationError: (zodError, _args, _context, _info) => {
|
|
// the default behavior is to just throw the zod error directly
|
|
Logger.error(zodError.message, 'Zod Error')
|
|
return zodError
|
|
},
|
|
},
|
|
relay: {},
|
|
prisma: {
|
|
client: prisma,
|
|
exposeDescriptions: true,
|
|
filterConnectionTotalCount: true,
|
|
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
|
|
dmmf: getDatamodel(),
|
|
},
|
|
errors: {
|
|
defaultTypes: [],
|
|
},
|
|
})
|
|
this.generator = new PrismaCrudGenerator<BuilderTypes>(this)
|
|
this.scalarType('DateTime', {
|
|
serialize: (value) => {
|
|
// Serialize outgoing DateTime to ISO string
|
|
if (typeof value === 'string') {
|
|
return value
|
|
}
|
|
if (typeof value === 'object' && value !== null && 'toISO' in value) {
|
|
return value
|
|
}
|
|
// if value = Date, convert to DateTime
|
|
if (value instanceof Date) {
|
|
return DateTimeUtils.toIsoString(DateTimeUtils.fromDate(value))
|
|
}
|
|
throw new Error('Invalid DateTime')
|
|
},
|
|
parseValue: (value) => {
|
|
// Parse incoming ISO string to Luxon DateTime
|
|
if (typeof value === 'string') {
|
|
return DateTimeUtils.fromIsoString(value)
|
|
}
|
|
throw new Error('Invalid DateTime')
|
|
},
|
|
parseLiteral: (ast) => {
|
|
// parse string to DateTime
|
|
if (ast.kind === Kind.STRING) {
|
|
return DateTimeUtils.fromIsoString(ast.value)
|
|
}
|
|
throw new Error('Invalid DateTime')
|
|
},
|
|
})
|
|
this.addScalarType('Json', JSONObjectResolver)
|
|
this.addScalarType('Upload', GraphQLUpload)
|
|
|
|
this.queryType({})
|
|
this.mutationType({})
|
|
this.subscriptionType({})
|
|
this.globalConnectionField('totalCount', (t) =>
|
|
t.int({
|
|
nullable: true,
|
|
resolve: (parent) =>
|
|
typeof parent.totalCount === 'function'
|
|
? parent.totalCount()
|
|
: parent.totalCount,
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
export type BuilderTypes =
|
|
PothosSchemaTypes.ExtendDefaultTypes<SchemaBuilderOption>
|