typegraphql-prisma-nestjs
Version:
This project is a fork of another with minor changes, created for personal use.
220 lines (211 loc) • 8.18 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateHelpersFile = generateHelpersFile;
const imports_1 = require("./imports");
function generateHelpersFile(sourceFile, options) {
(0, imports_1.generateGraphQLInfoImport)(sourceFile);
(0, imports_1.generateGraphQLFieldsImport)(sourceFile);
sourceFile.addImportDeclaration({
moduleSpecifier: "@nestjs/common",
namedImports: [
"CallHandler",
"createParamDecorator",
"ExecutionContext",
"Injectable",
"NestInterceptor",
"Type",
].sort(),
});
sourceFile.addImportDeclaration({
moduleSpecifier: "@nestjs/core",
namedImports: ["APP_INTERCEPTOR", "ContextIdFactory", "ModuleRef"].sort(),
});
sourceFile.addImportDeclaration({
moduleSpecifier: "dataloader",
namespaceImport: "DataLoader",
});
sourceFile.addImportDeclaration({
moduleSpecifier: "rxjs",
namedImports: ["Observable"].sort(),
});
sourceFile.addImportDeclaration({
moduleSpecifier: "@nestjs/graphql",
namedImports: ["GqlExecutionContext"].sort(),
});
sourceFile.addStatements(/* ts */ `
export function transformInfoIntoPrismaArgs(info: GraphQLResolveInfo, modelName?: string, collectionName?: string, prismaMethod?: string, isResolveField?: boolean): Record<string, any> {
const fields: Record<string, any> = graphqlFields(
// suppress GraphQLResolveInfo types issue
info as any,
{},
{
excludedFields: ['__typename'],
processArguments: true,
}
);
return transformFields(fields, modelName, collectionName, prismaMethod, isResolveField);
}
`);
sourceFile.addStatements(/* ts */ `
function transformFields(fields: Record<string, any>, modelName?: string, collectionName?: string, prismaMethod?: string, isResolveField?: boolean): Record<string, any> {
return Object.fromEntries(
Object.entries(fields)
.map<[string, any]>(([key, value]) => {
if (Object.keys(value).length === 0) {
return [key, true];
}
if ("__arguments" in value) {
return [key, Object.fromEntries(
value.__arguments.map((argument: object) => {
const [[key, { value }]] = Object.entries(argument);
return [key, value];
})
)];
}
return [key, transformFields(value, modelName, collectionName, prismaMethod, isResolveField)];
}),
);
}
`);
sourceFile.addStatements(/* ts */ `
export function getPrismaFromContext(context: any) {
const prismaClient = context["${options.contextPrismaKey}"];
if (!prismaClient) {
throw new Error("Unable to find Prisma Client in GraphQL context. Please provide it under the \`context[\\"${options.contextPrismaKey}\\"]\` key.");
}
return prismaClient;
}
`);
sourceFile.addStatements(/* ts */ `
export function transformCountFieldIntoSelectRelationsCount(_count: object, modelName?: string, collectionName?: string, prismaMethod?: string, isResolveField?: boolean) {
return {
include: {
_count: {
select: {
...Object.fromEntries(
Object.entries(_count).filter(([_, v]) => v != null)
),
}
},
},
}
}
`);
sourceFile.addStatements(/* ts */ `
export let transformArgsIntoPrismaArgs = async function <TArgs = Record<string, any>, TContext = any>(info: GraphQLResolveInfo, args: TArgs, ctx: TContext, modelName?: string, collectionName?: string, prismaMethod?: string, afterProcessEvents?: ((result:any) => Promise<any>)[], isResolveField?: boolean): Promise<TArgs> {
return args;
};
export function setTransformArgsIntoPrismaArgs(newTransformArgsIntoPrismaArgs: typeof transformArgsIntoPrismaArgs) {
transformArgsIntoPrismaArgs = newTransformArgsIntoPrismaArgs;
}
`);
sourceFile.addStatements(/* ts */ `
export class GraphqlError extends Error {}
/**
* This interface will be used to generate the initial data loader.
* The concrete implementation should be added as a provider to your module.
*/
export interface GraphqlDataLoader<ID, Type> {
/**
* Should return a new instance of dataloader each time
*/
generateDataLoader(context?: ExecutionContext): DataLoader<ID, Type>;
}
/**
* Context key where get loader function will be stored.
* This class should be added to your module providers like so:
* {
* provide: APP_INTERCEPTOR,
* useClass: DataLoaderInterceptor,
* },
*/
const NEST_LOADER_CONTEXT_KEY = 'NEST_LOADER_CONTEXT_KEY';
@Injectable()
export class GraphqlDataLoaderInterceptor implements NestInterceptor {
constructor(private readonly moduleRef: ModuleRef) {}
/**
* @inheritdoc
*/
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const graphqlExecutionContext = GqlExecutionContext.create(context);
const ctx = graphqlExecutionContext.getContext();
if (ctx[NEST_LOADER_CONTEXT_KEY] === undefined) {
ctx[NEST_LOADER_CONTEXT_KEY] = {
contextId: ContextIdFactory.create(),
getLoader: (type: string): Promise<GraphqlDataLoader<any, any>> => {
if (ctx[type] === undefined) {
try {
ctx[type] = (async () => {
return (
await this.moduleRef.resolve<GraphqlDataLoader<any, any>>(
type,
ctx[NEST_LOADER_CONTEXT_KEY].contextId,
{
strict: false,
}
)
).generateDataLoader(context);
})();
} catch (e) {
throw new GraphqlError(\`The loader \${type} is not provided\` + e);
}
}
return ctx[type];
},
};
}
return next.handle();
}
}
/**
* The decorator to be used within your graphql method.
*/
export const Loader = createParamDecorator(
async (data: Type<GraphqlDataLoader<any, any>>, context: ExecutionContext & { [key: string]: any }) => {
const ctx: any = GqlExecutionContext.create(context).getContext();
if (ctx[NEST_LOADER_CONTEXT_KEY] === undefined) {
throw new GraphqlError(\`
You should provide interceptor \${GraphqlDataLoaderInterceptor.name} globally with \${APP_INTERCEPTOR}
\`);
}
return await ctx[NEST_LOADER_CONTEXT_KEY].getLoader(data);
}
);
/**
* Easy way to create a dataloader in a specific location, context only needs to be used to access global properties that are set in each request.
* Added the ability to reuse simple dataloaders by a certain name.
*/
export const InlineLoader = createParamDecorator(
(
generateDataLoader: <ID, Type>(
context?: ExecutionContext
) => DataLoader<ID, Type> | { name: string; loader: DataLoader<ID, Type> },
context: ExecutionContext & { [key: string]: any }
) => {
const _class = context.getClass();
const _handler = context.getHandler();
const dataloader = generateDataLoader(context);
const ctx: any = GqlExecutionContext.create(context).getContext();
if (!ctx.InlineLoadersStorage) {
ctx.InlineLoadersStorage = new Map();
}
if ('loader' in dataloader) {
const dataloaderName = \`Dataloader: \${dataloader.name}\`;
if (!ctx.InlineLoadersStorage.has(dataloaderName)) {
ctx.InlineLoadersStorage.set(dataloaderName, dataloader.loader);
}
return ctx.InlineLoadersStorage.get(dataloaderName);
} else {
if (!ctx.InlineLoadersStorage.has(_class)) {
ctx.InlineLoadersStorage.set(_class, new Map());
}
if (!ctx.InlineLoadersStorage.get(_class).has(_handler)) {
ctx.InlineLoadersStorage.get(_class).set(_handler, dataloader);
}
return ctx.InlineLoadersStorage.get(_class).get(_handler);
}
}
);
`);
}
//# sourceMappingURL=generate-helpers.js.map