From 315c9d2c9c3bdafef9b645570f384eeac53d6291 Mon Sep 17 00:00:00 2001 From: Aleksandar Toplek Date: Mon, 11 Dec 2023 00:57:37 +0100 Subject: [PATCH] fix(doprocess): Creating new document for task definition would link to existing one Closes #4064 --- web/apps/doprocess/app/api/documents/route.ts | 2 +- .../[taskDefinitionId]/route.ts | 17 ++++++++----- web/apps/doprocess/src/lib/db/schema.ts | 14 +++++++---- .../src/lib/repo/documentsRepository.ts | 25 ++++++++++++++++--- .../src/lib/repo/processesRepository.ts | 24 +++++++++++++++--- web/apps/doprocess/src/lib/repo/shared.ts | 15 ++++++----- 6 files changed, 72 insertions(+), 25 deletions(-) diff --git a/web/apps/doprocess/app/api/documents/route.ts b/web/apps/doprocess/app/api/documents/route.ts index 47009a2378..a1c2cb5516 100644 --- a/web/apps/doprocess/app/api/documents/route.ts +++ b/web/apps/doprocess/app/api/documents/route.ts @@ -1,4 +1,4 @@ -import { documentCreate, documentGet, documentsGet, documentSetData } from '../../../src/lib/repo/documentsRepository'; +import { documentCreate, documentGet, documentsGet } from '../../../src/lib/repo/documentsRepository'; import { ensureUserId } from '../../../src/lib/auth/apiAuth'; export async function GET() { diff --git a/web/apps/doprocess/app/api/processes/[id]/task-definitions/[taskDefinitionId]/route.ts b/web/apps/doprocess/app/api/processes/[id]/task-definitions/[taskDefinitionId]/route.ts index 55b3b902ce..83b260f32e 100644 --- a/web/apps/doprocess/app/api/processes/[id]/task-definitions/[taskDefinitionId]/route.ts +++ b/web/apps/doprocess/app/api/processes/[id]/task-definitions/[taskDefinitionId]/route.ts @@ -44,30 +44,35 @@ export async function PUT(request: Request, { params }: { params: { id: string, const data = await request.json(); if (data != null && typeof data === 'object') { + // Update text (name) if ('text' in data && typeof data.text === 'string') { await changeTaskDefinitionText(userId, processId, taskDefinitionId, data.text); } + + // Update type if ('type' in data && typeof data.type === 'string') { - let typeData = 'typeData' in data && typeof data.typeData === 'string' ? data.typeData : null; - if (!typeData && data.type === 'document') { + const type = data.type; + let typeData = ('typeData' in data && typeof data.typeData === 'string') ? data.typeData : null; + if (!typeData && type === 'document') { const process = await getProcess(userId, processId); const taskDefinition = await getTaskDefinition(userId, processId, taskDefinitionId); - // TODO: Use publicId instead of internalId for dataType const documentName = [process?.name, taskDefinition?.text ?? 'New document'].filter(Boolean).join(' - '); const documentId = await documentCreate(userId, documentName); - const document = await documentGet(userId, Number(documentId)); + const document = await documentGet(userId, documentId); if (document) { typeData = document?.publicId; } - } else if (data.type === 'blank') { + } else if (type === 'blank') { typeData = 'blank'; } if (!typeData) { throw new Error('Invalid type'); } - await changeTaskDefinitionType(userId, processId, taskDefinitionId, data.type, typeData); + await changeTaskDefinitionType(userId, processId, taskDefinitionId, type, typeData); } + + // Update order if ('order' in data && typeof data.order === 'string') { await changeTaskDefinitionOrder(userId, processId, taskDefinitionId, data.order); } diff --git a/web/apps/doprocess/src/lib/db/schema.ts b/web/apps/doprocess/src/lib/db/schema.ts index 2a795b94e6..79b0bdfafd 100644 --- a/web/apps/doprocess/src/lib/db/schema.ts +++ b/web/apps/doprocess/src/lib/db/schema.ts @@ -1,9 +1,13 @@ import { datetime, index, int, json, mysqlTable, serial, text, uniqueIndex, varchar } from 'drizzle-orm/mysql-core'; import { relations, sql } from 'drizzle-orm'; +export const withPublicIdTable = { + publicId: varchar('public_id', { length: 32 }).notNull().unique(), +}; + export const process = mysqlTable('process', { id: serial('id').primaryKey(), - publicId: varchar('public_id', { length: 32 }).notNull().unique(), + ...withPublicIdTable, name: varchar('name', { length: 255 }).notNull(), sharedWithUsers: json('shared_with_users').$type().notNull(), createdBy: varchar('created_by', { length: 255 }).notNull(), @@ -18,7 +22,7 @@ export type Process = typeof process.$inferSelect; export const processRun = mysqlTable('process_run', { id: serial('id').primaryKey(), - publicId: varchar('public_id', { length: 32 }).notNull().unique(), + ...withPublicIdTable, processId: int('process_id').notNull(), name: varchar('name', { length: 255 }).notNull(), state: varchar('state', { length: 255 }).notNull(), @@ -35,7 +39,7 @@ export type ProcessRun = typeof processRun.$inferSelect; export const taskDefinition = mysqlTable('task_definition', { id: serial('id').primaryKey(), - publicId: varchar('public_id', { length: 32 }).notNull().unique(), + ...withPublicIdTable, processId: int('process_id').notNull(), text: text('text'), order: varchar('order', { length: 255 }).notNull(), @@ -54,7 +58,7 @@ export type TaskDefinition = typeof taskDefinition.$inferSelect; export const document = mysqlTable('document', { id: serial('id').primaryKey(), - publicId: varchar('public_id', { length: 32 }).notNull().unique(), + ...withPublicIdTable, name: text('name').notNull(), data: json('data'), sharedWithUsers: json('shared_with_users').$type().notNull(), @@ -81,7 +85,7 @@ export const taskDefinitionRelations = relations(taskDefinition, ({ one }) => ({ export const task = mysqlTable('task', { id: serial('id').primaryKey(), - publicId: varchar('public_id', { length: 32 }).notNull().unique(), + ...withPublicIdTable, processId: int('process_id').notNull(), runId: int('run_id').notNull(), taskDefinitionId: int('task_definition_id').notNull(), diff --git a/web/apps/doprocess/src/lib/repo/documentsRepository.ts b/web/apps/doprocess/src/lib/repo/documentsRepository.ts index aa6dd20fc2..60b3294703 100644 --- a/web/apps/doprocess/src/lib/repo/documentsRepository.ts +++ b/web/apps/doprocess/src/lib/repo/documentsRepository.ts @@ -17,11 +17,23 @@ function documentSharedWithUser(userId: string | null, includePublic = true) { } export async function getDocumentIdByPublicId(publicId: string) { - return firstOrDefault(await db.select({ id: document.id }).from(document).where(eq(document.publicId, publicId)))?.id; + return firstOrDefault(await db + .select({ id: document.id }) + .from(document) + .where( + eq(document.publicId, publicId)) + )?.id; } async function isDocumentSharedWithUser(userId: string, documentId: number) { - return (firstOrDefault(await db.select({ count: count() }).from(document).where(and(eq(document.id, documentId), documentSharedWithUser(userId))))?.count ?? 0) > 0; + return (firstOrDefault(await db + .select({ count: count() }) + .from(document) + .where( + and( + eq(document.id, documentId), + documentSharedWithUser(userId))) + )?.count ?? 0) > 0; } export async function documentCreate(userId: string, name: string, basedOn?: string) { @@ -38,6 +50,7 @@ export async function documentCreate(userId: string, name: string, basedOn?: str if (basedOnId) { const basedOnDocument = await documentGet(userId, basedOnId); if (basedOnDocument && typeof basedOnDocument.data === 'string') { + console.info('Copying document content from (public):', basedOnDocument.publicId, 'to (internal):', id); await documentSetData(userId, id, basedOnDocument.data); } } @@ -51,7 +64,13 @@ export async function documentsGet(userId: string) { } export async function documentGet(userId: string | null, id: number) { - return firstOrDefault(await db.select().from(document).where(and(eq(document.id, id), documentSharedWithUser(userId)))); + return firstOrDefault(await db + .select() + .from(document) + .where( + and( + eq(document.id, id), + documentSharedWithUser(userId)))); } export async function documentRename(userId: string, id: number, name: string) { diff --git a/web/apps/doprocess/src/lib/repo/processesRepository.ts b/web/apps/doprocess/src/lib/repo/processesRepository.ts index f6d0379969..2e4b22dbb8 100644 --- a/web/apps/doprocess/src/lib/repo/processesRepository.ts +++ b/web/apps/doprocess/src/lib/repo/processesRepository.ts @@ -22,7 +22,11 @@ async function isProcessSharedWithUser(userId: string | null, processId: number) return (firstOrDefault(await db .select({ count: count() }) .from(process) - .where(and(eq(process.id, processId), processSharedWithUser(userId))))?.count ?? 0) > 0; + .where( + and( + eq(process.id, processId), + processSharedWithUser(userId))) + )?.count ?? 0) > 0; } export async function getProcessIdByPublicId(publicId: string) { @@ -195,7 +199,13 @@ export async function getTaskDefinitions(userId: string | null, processId: numbe export async function getTaskDefinition(userId: string | null, processId: number, taskDefinitionId: number) { if (!await isProcessSharedWithUser(userId, processId)) throw new Error('Not found'); - return firstOrDefault(await db.select().from(taskDefinition).where(and(eq(taskDefinition.processId, processId), eq(taskDefinition.id, taskDefinitionId)))); + return firstOrDefault(await db + .select() + .from(taskDefinition) + .where( + and( + eq(taskDefinition.processId, processId), + eq(taskDefinition.id, taskDefinitionId)))); } async function getTaskDefinitionLastOrder(userId: string, processId: number) { @@ -226,11 +236,17 @@ export async function changeTaskDefinitionText(userId: string, processId: number await db.update(taskDefinition).set({ text, updatedBy: userId, updatedAt: new Date() }).where(and(eq(taskDefinition.processId, processId), eq(taskDefinition.id, id))); } -export async function changeTaskDefinitionType(userId: string, processId: number, id: number, type: string, typeData: string) { +export async function changeTaskDefinitionType(userId: string, processId: number, id: number, type: string, typeData: string | null) { if (!await isProcessSharedWithUser(userId, processId)) throw new Error('Not found'); // TODO: Check permissions - await db.update(taskDefinition).set({ type, typeData, updatedBy: userId, updatedAt: new Date() }).where(and(eq(taskDefinition.processId, processId), eq(taskDefinition.id, id))); + await db + .update(taskDefinition) + .set({ type: type, typeData: typeData, updatedBy: userId, updatedAt: new Date() }) + .where( + and( + eq(taskDefinition.processId, processId), + eq(taskDefinition.id, id))); } export async function changeTaskDefinitionOrder(userId: string, processId: number, id: number, order: string) { diff --git a/web/apps/doprocess/src/lib/repo/shared.ts b/web/apps/doprocess/src/lib/repo/shared.ts index 3d1bc2abab..e0e007f5d4 100644 --- a/web/apps/doprocess/src/lib/repo/shared.ts +++ b/web/apps/doprocess/src/lib/repo/shared.ts @@ -1,15 +1,18 @@ import { nanoid } from 'nanoid'; -import { sql, eq } from 'drizzle-orm'; +import { AnyMySqlTable } from 'drizzle-orm/mysql-core'; +import { count, sql } from 'drizzle-orm'; import { firstOrDefault } from '@signalco/js'; import { db } from '../db'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function publicIdExists(entity: any, publicId: string) { - return (firstOrDefault(await db.select({ count: sql`count(*)` }).from(entity).where(eq(entity.publicId, publicId)))?.count ?? 0) > 0; +export async function publicIdExists(entity: AnyMySqlTable, publicId: string) { + return (firstOrDefault(await db + .select({ count: count() }) + .from(entity) + .where(sql`publicId = ${publicId}`) + )?.count ?? 0) > 0; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function publicIdNext(entity: any, size = 21) { +export async function publicIdNext(entity: AnyMySqlTable, size = 21) { let publicId = undefined; while (!publicId || await publicIdExists(entity, publicId)) publicId = nanoid(size);