typegraphql-prisma-nestjs
Version:
This project is a fork of another with minor changes, created for personal use.
254 lines • 13.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = generateRelationsResolverClassesFromModel;
const tslib_1 = require("tslib");
const ts_morph_1 = require("ts-morph");
const path_1 = tslib_1.__importDefault(require("path"));
const helpers_1 = require("../helpers");
const config_1 = require("../config");
const imports_1 = require("../imports");
function generateRelationsResolverClassesFromModel(project, baseDirPath, dmmfDocument, { model, relationFields, resolverName }, generatorOptions) {
const rootArgName = (0, helpers_1.camelCase)(model.typeName);
const singleIdField = model.fields.find(field => field.isId);
const singleUniqueField = model.fields.find(field => field.isUnique);
const singleFilterField = singleIdField ?? singleUniqueField;
const compositeIdFields = model.primaryKey?.fields.map(idField => model.fields.find(field => idField === field.name)) ?? [];
const compositeUniqueFields = model.uniqueIndexes[0]
? model.uniqueIndexes[0].fields.map(uniqueField => model.fields.find(field => uniqueField === field.name))
: [];
const compositeFilterFields = compositeIdFields.length > 0 ? compositeIdFields : compositeUniqueFields;
const resolverDirPath = path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.relationsResolversFolderName, model.typeName);
const filePath = path_1.default.resolve(resolverDirPath, `${resolverName}.ts`);
const sourceFile = project.createSourceFile(filePath, undefined, {
overwrite: true,
});
(0, imports_1.generateTypeGraphQLImport)(sourceFile);
(0, imports_1.generateGraphQLInfoImport)(sourceFile);
(0, imports_1.generateModelsImports)(sourceFile, [...relationFields.map(field => field.type), model.typeName], 3);
const argTypeNames = relationFields
.filter(it => it.argsTypeName !== undefined)
.map(it => it.argsTypeName);
(0, imports_1.generateArgsImports)(sourceFile, argTypeNames, 0);
(0, imports_1.generateHelpersFileImport)(sourceFile, 3);
sourceFile.addImportDeclaration({
moduleSpecifier: "dataloader",
namespaceImport: "DataLoader",
});
sourceFile.addClass({
name: resolverName,
isExported: true,
decorators: [
{
name: "Resolver",
arguments: [`_of => ${model.typeName}`],
},
],
methods: relationFields.map(field => {
let whereConditionString = "";
// TODO: refactor to AST
if (singleFilterField) {
whereConditionString = `
${singleFilterField.name}: ${rootArgName}.${singleFilterField.name},
`;
}
else if (compositeFilterFields.length > 0) {
const filterKeyName = model.primaryKey?.name ??
model.uniqueIndexes[0]?.name ??
compositeFilterFields.map(it => it.name).join("_");
whereConditionString = `
${filterKeyName}: {
${compositeFilterFields
.map(idField => `${idField.name}: ${rootArgName}.${idField.name},`)
.join("\n")}
},
`;
}
else {
throw new Error(`Unexpected error happened on generating 'whereConditionString' for ${model.typeName} relation resolver`);
}
const relationFromField = dmmfDocument.relationModels
.find(m => m.model.name === field.name)
?.model?.fields?.find(f => f?.name === model?.name)
?.relationFromFields?.[0];
const relationFromFieldType = dmmfDocument.relationModels
.find(m => m.model.name === field.name)
?.model?.fields?.find(f => f?.isId)?.fieldTSType;
if (generatorOptions.useDataloaderForAllResolveFields ||
(generatorOptions.useDataloaderForResolveFields &&
!field.argsTypeName)) {
const datamapperOptions = [
generatorOptions.useDataloaderMaxBatchSize !== undefined
? `maxBatchSize: ${generatorOptions.useDataloaderMaxBatchSize}`
: undefined,
generatorOptions.useDataloaderCache !== undefined
? `cache: ${generatorOptions.useDataloaderCache}`
: undefined,
generatorOptions.useDataloaderBatchScheduleFnDelay !== undefined
? `batchScheduleFn: (cb) => setTimeout(() => process.nextTick(cb), ${generatorOptions.useDataloaderBatchScheduleFnDelay})`
: undefined,
].filter(Boolean);
const datamapperOptionsText = datamapperOptions.length
? `, {${datamapperOptions.join(",")}}`
: "";
return {
name: field.typeFieldAlias ?? field.name,
isAsync: true,
returnType: `Promise<${field.fieldTSType}>`,
decorators: [
{
name: "ResolveField",
arguments: [
`_type => ${field.typeGraphQLType}`,
ts_morph_1.Writers.object({
nullable: `${!field.isRequired}`,
...(field.docs && { description: `"${field.docs}"` }),
}),
],
},
],
parameters: [
{
name: rootArgName,
type: model.typeName,
decorators: [{ name: "Root", arguments: [] }],
},
{
name: "ctx",
// TODO: import custom `ContextType`
type: "any",
decorators: [{ name: "Context", arguments: [] }],
},
{
name: "info",
type: "GraphQLResolveInfo",
decorators: [{ name: "Info", arguments: [] }],
},
...((!field.argsTypeName)
? []
: [
{
name: "args",
type: field.argsTypeName,
decorators: [
{
name: "Args",
arguments: generatorOptions.emitRedundantTypesInfo
? [`_type => ${field.argsTypeName}`]
: [],
},
],
},
]),
{
name: "dataloader",
type: `DataLoader<${relationFromFieldType || "string"}, ${field.typeFieldAlias ?? field.type}${field.isList ? "[]" : ""}>`,
decorators: [
{
name: "InlineLoader",
arguments: [
`<ID,Type>(context)=>{
const graphqlExecutionContext = GqlExecutionContext.create(context);
const ctx = graphqlExecutionContext.getContext();
const loader = new DataLoader<ID,Type>(
async (ids) => {
const context = (loader as any).context;
const info = (loader as any).info;
const args = (loader as any).args;
const { _count } = transformInfoIntoPrismaArgs(info, '${model.name}', '${(0, helpers_1.camelCase)(model.name)}', 'findMany', true);
const transformedArgsIntoPrismaArgs = await transformArgsIntoPrismaArgs(info, args, context, '${model.name}', '${(0, helpers_1.camelCase)(model.name)}', 'findMany', [], true);
const otherArgs = _count && transformCountFieldIntoSelectRelationsCount(_count, '${model.name}', '${(0, helpers_1.camelCase)(model.name)}', 'findMany', true);
const allArgs = { ...transformedArgsIntoPrismaArgs, ...otherArgs, };
const result:${field.type}[] = await getPrismaFromContext(ctx).${(0, helpers_1.camelCase)(field.type)}.findMany({
...allArgs,
where: {
...(allArgs.where || {}),
${relationFromField || field.relationToFields?.[0] || "id"}: { in: ids },
},
});
return ids.map(id=>result.${field.isList ? "filter" : "find"}(r=>r.${relationFromField || field.relationToFields?.[0] || "id"}===id)||${field.isList ? "[]" : "null"}) as Type[]
}${datamapperOptionsText}
);
return loader;
}`,
],
},
],
},
],
// TODO: refactor to AST
statements: [
"(dataloader as any).info = info;",
"(dataloader as any).context = ctx;",
(!field.argsTypeName)
? "(dataloader as any).args = {};"
: "(dataloader as any).args = args;",
field.isRequired
? ` return await dataloader.load(${rootArgName}.${field.relationFromFields?.[0] || "id"});`
: /* ts */ ` return !${rootArgName}.${field.relationFromFields?.[0] || "id"}?${field.isList ? "[]" : "null"}:await dataloader.load(${rootArgName}.${field.relationFromFields?.[0] || "id"});`,
],
};
}
return {
name: field.typeFieldAlias ?? field.name,
isAsync: true,
returnType: `Promise<${field.fieldTSType}>`,
decorators: [
{
name: "ResolveField",
arguments: [
`_type => ${field.typeGraphQLType}`,
ts_morph_1.Writers.object({
nullable: `${!field.isRequired}`,
...(field.docs && { description: `"${field.docs}"` }),
}),
],
},
],
parameters: [
{
name: rootArgName,
type: model.typeName,
decorators: [{ name: "Root", arguments: [] }],
},
{
name: "ctx",
// TODO: import custom `ContextType`
type: "any",
decorators: [{ name: "Context", arguments: [] }],
},
{
name: "info",
type: "GraphQLResolveInfo",
decorators: [{ name: "Info", arguments: [] }],
},
...(!field.argsTypeName
? []
: [
{
name: "args",
type: field.argsTypeName,
decorators: [
{
name: "Args",
arguments: generatorOptions.emitRedundantTypesInfo
? [`_type => ${field.argsTypeName}`]
: [],
},
],
},
]),
],
// TODO: refactor to AST
statements: [
/* ts */ ` const { _count } = transformInfoIntoPrismaArgs(info, '${model.name}', '', '');
return getPrismaFromContext(ctx).${(0, helpers_1.camelCase)(model.name)}.findUniqueOrThrow({
where: {${whereConditionString}},
}).${field.name}({ ${field.argsTypeName ? "\n...args," : ""}
...(_count && transformCountFieldIntoSelectRelationsCount(_count, '${model.name}', '', '')),
});`,
],
};
}),
});
}
//# sourceMappingURL=relations.js.map