From 65979a0f2880c6dad0a137a55d6298bbd6d10f8c Mon Sep 17 00:00:00 2001 From: Dirk de Visser Date: Wed, 10 May 2023 16:15:22 +0200 Subject: [PATCH] feat(code-gen): add support for `transformContext` with custom readable types in CRUD - Fixes checks around custom readable types in `T.crud()` - Add `xxxTransformContext` as a hook. The returned `transformContext` is then passed to `xxxTransform`. This can be used to pass ctx based data to the transform function (like the base url). - Fixes using the custom readable type in the create route --- packages/code-gen/src/builders/CrudType.js | 2 +- packages/code-gen/src/crud/events.js | 6 -- packages/code-gen/src/crud/handlers.js | 19 +++++- .../code-gen/src/crud/partials/events.d.ts | 1 - packages/code-gen/src/crud/partials/events.js | 5 +- .../code-gen/src/crud/partials/routes.d.ts | 4 ++ packages/code-gen/src/crud/partials/routes.js | 65 +++++++++++++++---- 7 files changed, 75 insertions(+), 27 deletions(-) diff --git a/packages/code-gen/src/builders/CrudType.js b/packages/code-gen/src/builders/CrudType.js index f540f3c437..a747edaf47 100644 --- a/packages/code-gen/src/builders/CrudType.js +++ b/packages/code-gen/src/builders/CrudType.js @@ -130,7 +130,7 @@ export class CrudType extends TypeBuilder { const { readable, writable } = fieldOptions; if (readable instanceof TypeBuilder) { - if (!readable.data.name) { + if (!readable.data.name && !readable.data.reference?.name) { throw AppError.serverError({ message: "A custom readable type should have a name, e.g 'T.object('item').keys(...)'.", diff --git a/packages/code-gen/src/crud/events.js b/packages/code-gen/src/crud/events.js index 5aed6d2dc2..97b105ace5 100644 --- a/packages/code-gen/src/crud/events.js +++ b/packages/code-gen/src/crud/events.js @@ -23,7 +23,6 @@ import { crudPartialEventTransformer, crudPartialEventUpdate, } from "./partials/events.js"; -import { crudQueryBuilderGet } from "./query-builder.js"; /** * Generate events that are necessary for CRUD. This currently only works with js and Koa. @@ -189,11 +188,6 @@ function crudEventsCreate(generateContext, file, crud) { writableType: crudInformationGetWritableType(crud), inlineRelations: crudEventsGetInlineRelations(crud), - builder: crudQueryBuilderGet(crud, { - includeOwnParam: false, - includeJoins: true, - traverseParents: false, - }), }; // @ts-expect-error diff --git a/packages/code-gen/src/crud/handlers.js b/packages/code-gen/src/crud/handlers.js index 2a4cec60d5..1b05f5e320 100644 --- a/packages/code-gen/src/crud/handlers.js +++ b/packages/code-gen/src/crud/handlers.js @@ -123,7 +123,10 @@ function crudHandlersGetModifiers(crud) { if (crudInformationGetHasCustomReadableType(crud)) { modifierDocs.push( - `${crudName}Transform: (entity: QueryResult${modelUniqueName}) => ${upperCaseFirst( + `${crudName}TransformContext?: (ctx: Context) => (any|Promise),`, + ); + modifierDocs.push( + `${crudName}Transform: (entity: QueryResult${modelUniqueName}, transformContext?: any) => ${upperCaseFirst( // @ts-expect-error crud.fieldOptions.readableType.reference.group, )}${upperCaseFirst( @@ -131,6 +134,8 @@ function crudHandlersGetModifiers(crud) { crud.fieldOptions.readableType.reference.name, )},`, ); + + modifierDestructure.push(`${crudName}TransformContext,`); modifierDestructure.push(`${crudName}Transform,`); } @@ -150,8 +155,8 @@ function crudHandlersGetModifiers(crud) { if (crud.routeOptions.createRoute) { modifierDocs.push( - `${crudName}CreatePreModifier?: (event: InsightEvent, ctx: ${upperCrudName}CreateCtx ${ - relation?.subType === "oneToMany" + `${crudName}CreatePreModifier?: (event: InsightEvent, ctx: ${upperCrudName}CreateCtx, builder: ${modelUniqueName}QueryBuilder${ + relation?.subType !== "oneToOneReverse" ? "" : `, singleBuilder: ${modelUniqueName}QueryBuilder` }) => void|Promise,`, @@ -238,6 +243,7 @@ function crudHandlersList(generateContext, file, crud) { crud, "list", )}`, + hasTransformContext: crudInformationGetHasCustomReadableType(crud), crudName: crud.group + upperCaseFirst(crudInformationGetName(crud, "")), countBuilder: crudQueryBuilderGet(crud, { includeOwnParam: false, @@ -290,6 +296,7 @@ function crudHandlersSingle(generateContext, file, crud) { "single", )}`, crudName: crud.group + upperCaseFirst(crudInformationGetName(crud, "")), + hasTransformContext: crudInformationGetHasCustomReadableType(crud), builder: crudQueryBuilderGet(crud, { includeOwnParam: true, includeJoins: true, @@ -326,12 +333,18 @@ function crudHandlersCreate(generateContext, file, crud) { "create", )}`, crudName: crud.group + upperCaseFirst(crudInformationGetName(crud, "")), + hasTransformContext: crudInformationGetHasCustomReadableType(crud), applyParams: crud.fromParent ? { bodyKey: relation.referencedKey, paramsKey: crudInformationGetParamName(parent), } : undefined, + builder: crudQueryBuilderGet(crud, { + includeOwnParam: false, + includeJoins: true, + traverseParents: false, + }), oneToOneChecks: relation?.subType === "oneToOneReverse" ? { diff --git a/packages/code-gen/src/crud/partials/events.d.ts b/packages/code-gen/src/crud/partials/events.d.ts index 2297663fcc..e0d657f10d 100644 --- a/packages/code-gen/src/crud/partials/events.d.ts +++ b/packages/code-gen/src/crud/partials/events.d.ts @@ -19,7 +19,6 @@ export function crudPartialEventCreate(data: { crudName: string; entityUniqueName: string; entityName: string; - builder: string; primaryKey: string; writableType: { group: string; diff --git a/packages/code-gen/src/crud/partials/events.js b/packages/code-gen/src/crud/partials/events.js index 4a407a6fda..dfb1b54186 100644 --- a/packages/code-gen/src/crud/partials/events.js +++ b/packages/code-gen/src/crud/partials/events.js @@ -112,7 +112,6 @@ export async function ${data.crudName}Single(event, sql, builder) { * crudName: string, * entityUniqueName: string, * entityName: string, - * builder: string, * primaryKey: string, * writableType: { group: string, name: string }, * inlineRelations: { @@ -137,9 +136,10 @@ export const crudPartialEventCreate = (data) => ` * @param {${upperCaseFirst(data.writableType.group)}${upperCaseFirst( data.writableType.name, )}} body + * @param {${data.entityUniqueName}QueryBuilder} builder * @returns {Promise} */ -export async function ${data.crudName}Create(event, sql, body) { +export async function ${data.crudName}Create(event, sql, body, builder) { eventStart(event, "${data.crudName}.create"); ${partialAsString( @@ -156,7 +156,6 @@ export async function ${data.crudName}Create(event, sql, body) { ${crudPartialInlineRelationInserts(data.inlineRelations, "result")} - const builder = ${data.builder}; builder.where.${data.primaryKey} = result[0].${data.primaryKey}; const _item = await ${ data.crudName diff --git a/packages/code-gen/src/crud/partials/routes.d.ts b/packages/code-gen/src/crud/partials/routes.d.ts index 39d51734b2..a72ca35516 100644 --- a/packages/code-gen/src/crud/partials/routes.d.ts +++ b/packages/code-gen/src/crud/partials/routes.d.ts @@ -1,18 +1,22 @@ export function crudPartialRouteList(data: { handlerName: string; crudName: string; + hasTransformContext: boolean; countBuilder: string; listBuilder: string; primaryKey: string; }): string; export function crudPartialRouteSingle(data: { handlerName: string; + hasTransformContext: boolean; crudName: string; builder: string; }): string; export function crudPartialRouteCreate(data: { handlerName: string; crudName: string; + hasTransformContext: boolean; + builder: string; applyParams?: { bodyKey: string; paramsKey: string; diff --git a/packages/code-gen/src/crud/partials/routes.js b/packages/code-gen/src/crud/partials/routes.js index 1f41507a8f..6dafca582d 100644 --- a/packages/code-gen/src/crud/partials/routes.js +++ b/packages/code-gen/src/crud/partials/routes.js @@ -2,6 +2,7 @@ * @param {{ * handlerName: string, * crudName: string, + * hasTransformContext: boolean, * countBuilder: string, * listBuilder: string, * primaryKey: string, @@ -19,17 +20,31 @@ ${data.handlerName} = async (ctx) => { const listBuilder = ${data.listBuilder}; - ${data.crudName}ListPreModifier && await ${data.crudName}ListPreModifier(newEventFromEvent(ctx.event), ctx, countBuilder, listBuilder); + ${data.crudName}ListPreModifier && await ${ + data.crudName +}ListPreModifier(newEventFromEvent(ctx.event), ctx, countBuilder, listBuilder); - const { total, ${data.primaryKey}In } = await ${data.crudName}Count(newEventFromEvent(ctx.event), sql, countBuilder, ctx.validatedQuery); + const { total, ${data.primaryKey}In } = await ${ + data.crudName +}Count(newEventFromEvent(ctx.event), sql, countBuilder, ctx.validatedQuery); listBuilder.where.${data.primaryKey}In = ${data.primaryKey}In; - const result = await ${data.crudName}List(newEventFromEvent(ctx.event), sql, listBuilder); + const result = await ${ + data.crudName + }List(newEventFromEvent(ctx.event), sql, listBuilder); + + ${ + data.hasTransformContext + ? `const transformContext = ${data.crudName}TransformContext ? await ${data.crudName}TransformContext(ctx) : undefined;` + : "" + } ctx.body = { total, - list: result.map(it => ${data.crudName}Transform(it)), + list: result.map(it => ${data.crudName}Transform(it${ + data.hasTransformContext ? ", transformContext" : "" +})), }; }; `; @@ -37,6 +52,7 @@ ${data.handlerName} = async (ctx) => { /** * @param {{ * handlerName: string, + * hasTransformContext: boolean, * crudName: string, * builder: string, * }} data @@ -46,13 +62,25 @@ export const crudPartialRouteSingle = (data) => ` ${data.handlerName} = async (ctx) => { const builder = ${data.builder}; - ${data.crudName}SinglePreModifier && await ${data.crudName}SinglePreModifier(newEventFromEvent(ctx.event), ctx, builder); + ${data.crudName}SinglePreModifier && await ${ + data.crudName +}SinglePreModifier(newEventFromEvent(ctx.event), ctx, builder); - const item = await ${data.crudName}Single(newEventFromEvent(ctx.event), sql, builder); + const item = await ${ + data.crudName + }Single(newEventFromEvent(ctx.event), sql, builder); + + ${ + data.hasTransformContext + ? `const transformContext = ${data.crudName}TransformContext ? await ${data.crudName}TransformContext(ctx) : undefined;` + : "" + } ctx.body = { - item: ${data.crudName}Transform(item), + item: ${data.crudName}Transform(item${ + data.hasTransformContext ? ", transformContext" : "" +}), }; }; `; @@ -61,6 +89,8 @@ ${data.handlerName} = async (ctx) => { * @param {{ * handlerName: string, * crudName: string, + * hasTransformContext: boolean, + * builder: string, * applyParams?: { * bodyKey: string, * paramsKey: string, @@ -73,15 +103,16 @@ ${data.handlerName} = async (ctx) => { */ export const crudPartialRouteCreate = (data) => ` ${data.handlerName} = async (ctx) => { + const builder = ${data.builder}; ${ data.oneToOneChecks - ? `const builder = ${data.oneToOneChecks.builder};` + ? `const oneToOneBuilder = ${data.oneToOneChecks.builder};` : `` } ${data.crudName}CreatePreModifier && await ${ data.crudName -}CreatePreModifier(newEventFromEvent(ctx.event), ctx ${ - data.oneToOneChecks ? `, builder` : "" +}CreatePreModifier(newEventFromEvent(ctx.event), ctx, builder${ + data.oneToOneChecks ? `, oneToOneBuilder` : "" }); ${ @@ -93,7 +124,7 @@ ${data.handlerName} = async (ctx) => { data.oneToOneChecks ? ` try { - const exists = await ${data.crudName}Single(newEventFromEvent(ctx.event), sql, builder); + const exists = await ${data.crudName}Single(newEventFromEvent(ctx.event), sql, oneToOneBuilder); if (exists) { throw AppError.validationError("${data.crudName}.create.alreadyExists"); } @@ -108,10 +139,18 @@ ${data.handlerName} = async (ctx) => { const item = await sql.begin(sql => ${ data.crudName - }Create(newEventFromEvent(ctx.event), sql, ctx.validatedBody)); + }Create(newEventFromEvent(ctx.event), sql, ctx.validatedBody, builder)); + ${ + data.hasTransformContext + ? `const transformContext = ${data.crudName}TransformContext ? await ${data.crudName}TransformContext(ctx) : undefined;` + : "" + } + ctx.body = { - item: ${data.crudName}Transform(item), + item: ${data.crudName}Transform(item${ + data.hasTransformContext ? ", transformContext" : "" +}), }; }; `;