diff --git a/.changeset/blue-ghosts-rush.md b/.changeset/blue-ghosts-rush.md new file mode 100644 index 00000000..4d4eb65d --- /dev/null +++ b/.changeset/blue-ghosts-rush.md @@ -0,0 +1,6 @@ +--- +'@kivotos/core': patch +--- + +[[DRIZZ-86] Embed __pk to relation field](https://app.plane.so/softnetics/browse/DRIZZ-86/) +[[DRIZZ-85] withValidator not working properly due to wrong response](https://app.plane.so/softnetics/browse/DRIZZ-85/) diff --git a/packages/core/src/builder.handler.ts b/packages/core/src/builder.handler.ts index 3ba0bb8a..6a8d83d5 100644 --- a/packages/core/src/builder.handler.ts +++ b/packages/core/src/builder.handler.ts @@ -7,7 +7,6 @@ import { Many, One, or, - sql, type TableRelationalConfig, } from 'drizzle-orm' import type { NodePgQueryResultHKT } from 'drizzle-orm/node-postgres' @@ -42,25 +41,18 @@ export function createDefaultApiHandlers< schema: Record fields: TFields tableTsKey: string - identiferColumn: string + identifierColumn: string tableNamesMap: Record tables: Record }): CollectionAdminApi { - const { fields, tableTsKey, tableNamesMap, tables, schema, identiferColumn } = args + const { fields, tableTsKey, tableNamesMap, tables, schema, identifierColumn } = args const tableRelationalConfig = tables[tableTsKey] const primaryKeyColumn = getPrimaryColumn(tableRelationalConfig) - const identifierKeyColumn = tableRelationalConfig.columns[identiferColumn] + const identifierKeyColumn = tableRelationalConfig.columns[identifierColumn] const tableName = tableRelationalConfig.tsName const tableSchema = getTableFromSchema(schema, tableTsKey) - const queryPayload = createDrizzleQuery(fields) - - const extrasQuery = { - extras: { - __pk: sql`${primaryKeyColumn}`.as('__pk'), - __id: sql`${identifierKeyColumn}`.as('__id'), - }, - } + const queryPayload = createDrizzleQuery(fields, tables, tableRelationalConfig, identifierColumn) const findOne: ApiFindOneHandler = async (args) => { const db = args.context.db @@ -68,7 +60,6 @@ export function createDefaultApiHandlers< const result = await query.findFirst({ ...queryPayload, - ...extrasQuery, where: eq(primaryKeyColumn, args.id), }) if (!result) { @@ -94,7 +85,6 @@ export function createDefaultApiHandlers< const result = await query.findMany({ ...queryPayload, - ...extrasQuery, limit: args.limit, offset: args.offset, orderBy: orderBy @@ -136,7 +126,6 @@ export function createDefaultApiHandlers< const result = await query.findFirst({ ...queryPayload, - ...extrasQuery, where: eq(primaryKeyColumn, pk), }) @@ -165,7 +154,6 @@ export function createDefaultApiHandlers< const pk = await apiHandler.update(args.id, tx, args.data) const result = await query.findFirst({ ...queryPayload, - ...extrasQuery, where: eq(primaryKeyColumn, pk), }) @@ -574,11 +562,11 @@ function mapResultToFields(fields: Fields, result: Record): Re const value = result[field._.relationTsName] if (!value) return [] - const primaryColumnTsName = field._.primaryColumnTsName if (Array.isArray(value)) { const values = value.map((v) => ({ ...mapResultToFields(field.fields, v), - __pk: v[primaryColumnTsName], + __pk: v['__pk'], + __id: v['__id'], })) return [[field.fieldName, values]] } @@ -586,7 +574,7 @@ function mapResultToFields(fields: Fields, result: Record): Re return [ [ field.fieldName, - { ...mapResultToFields(field.fields, value), __pk: value[primaryColumnTsName] }, + { ...mapResultToFields(field.fields, value), __pk: value['__pk'], __id: value['__id'] }, ], ] } diff --git a/packages/core/src/builder.ts b/packages/core/src/builder.ts index 92b7d6da..ca7c9a75 100644 --- a/packages/core/src/builder.ts +++ b/packages/core/src/builder.ts @@ -82,7 +82,7 @@ export class Builder< const defaultHandlers = createDefaultApiHandlers({ schema: this.config.schema, fields: config.fields, - identiferColumn: config.identifierColumn as string, + identifierColumn: config.identifierColumn as string, tableTsKey: tableTsName, tables: this.tableRelationalConfigByTableTsName, tableNamesMap: this.tableTsNameByTableDbName, diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index af5d9cdf..cebde50a 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -1,7 +1,7 @@ -import type { Column, TableRelationalConfig } from 'drizzle-orm' -import { is, Table } from 'drizzle-orm' +import type { Column, SQL, TableRelationalConfig } from 'drizzle-orm' +import { is, sql, Table } from 'drizzle-orm' import type { IsNever, Simplify, ValueOf } from 'type-fest' -import type { ZodError, ZodObject, ZodOptional, ZodType } from 'zod' +import type { ZodIssue, ZodObject, ZodOptional, ZodType } from 'zod' import type { ApiHttpStatus, @@ -74,7 +74,28 @@ export function getTableFromSchema(schema: Record, tableTsName: return schema[tableTsName] } -export function createDrizzleQuery(fields: Fields): Record { +const getExtraField = (tableRelational: TableRelationalConfig, identifierColumn?: string) => { + const extraWith: [string, SQL.Aliased][] = [] + + const primaryKeyColumn = tableRelational.primaryKey[0] + + extraWith.push(['__pk', sql`${primaryKeyColumn}`.as('__pk') as SQL.Aliased]) + + if (identifierColumn) { + const identifierKeyColumn = tableRelational.columns[identifierColumn] + + extraWith.push(['__id', sql`${identifierKeyColumn}`.as('__id') as SQL.Aliased]) + } + + return Object.fromEntries(extraWith) +} + +export function createDrizzleQuery( + fields: Fields, + table: Record, + tableRelationalConfig: TableRelationalConfig, + identifierColumn?: string +): Record { const queryColumns = Object.fromEntries( Object.values(fields).flatMap((field) => { if (field._.source !== 'column') return [] @@ -86,14 +107,18 @@ export function createDrizzleQuery(fields: Fields): Record { Object.values(fields).flatMap((field) => { if (!isRelationField(field)) return [] const relationName = field._.relation.fieldName + const referencedTableName = field._.relation.referencedTableName - return [[relationName, createDrizzleQuery(field.fields) as any]] + return [ + [relationName, createDrizzleQuery(field.fields, table, table[referencedTableName]) as any], + ] }) ) return { columns: queryColumns, with: queryWith, + extras: getExtraField(tableRelationalConfig, identifierColumn), } } @@ -125,14 +150,16 @@ export async function validateRequestBody< TApiRouteSchema extends ApiRouteSchema = any, TContext extends Record = Record, >(schema: TApiRouteSchema, payload: ApiRouteHandlerPayloadWithContext) { - let zodErrors: Partial> | undefined + let zodErrors: + | Partial> + | undefined if (schema.query) { const err = await schema.query.safeParseAsync((payload as any).query) if (!err.success) { zodErrors = { ...zodErrors, - query: err.error, + query: err.error.issues, } } } @@ -142,7 +169,7 @@ export async function validateRequestBody< if (!err.success) { zodErrors = { ...zodErrors, - pathParams: err.error, + pathParams: err.error.issues, } } } @@ -152,7 +179,7 @@ export async function validateRequestBody< if (!err.success) { zodErrors = { ...zodErrors, - headers: err.error, + headers: err.error.issues, } } } @@ -162,7 +189,7 @@ export async function validateRequestBody< if (!err.success) { zodErrors = { ...zodErrors, - body: err.error, + body: err.error.issues, } } } @@ -217,7 +244,7 @@ export function withValidator< status: 500, body: { error: 'Response validation failed', - details: validationError, + details: validationError.issues, }, } }