From c6489476f93e3e6d322fa0f9a871b4f45052fdec Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Thu, 8 Feb 2024 11:03:08 +0100 Subject: [PATCH] :bug: (editor) Fix lost changes when typebot takes a long time to update Closes #1231 --- .../editor/providers/TypebotProvider.tsx | 6 +- .../src/features/typebot/api/updateTypebot.ts | 251 +++++++++--------- apps/docs/openapi/builder.json | 10 +- 3 files changed, 134 insertions(+), 133 deletions(-) diff --git a/apps/builder/src/features/editor/providers/TypebotProvider.tsx b/apps/builder/src/features/editor/providers/TypebotProvider.tsx index 99c3953d4..e95606e39 100644 --- a/apps/builder/src/features/editor/providers/TypebotProvider.tsx +++ b/apps/builder/src/features/editor/providers/TypebotProvider.tsx @@ -208,7 +208,11 @@ export const TypebotProvider = ({ const saveTypebot = useCallback( async (updates?: Partial) => { if (!localTypebot || !typebot || isReadOnly) return - const typebotToSave = { ...localTypebot, ...updates } + const typebotToSave = { + ...localTypebot, + ...updates, + updatedAt: new Date(), + } if (dequal(omit(typebot, 'updatedAt'), omit(typebotToSave, 'updatedAt'))) return setLocalTypebot({ ...typebotToSave }) diff --git a/apps/builder/src/features/typebot/api/updateTypebot.ts b/apps/builder/src/features/typebot/api/updateTypebot.ts index bfa25dd7e..ce53d5cb3 100644 --- a/apps/builder/src/features/typebot/api/updateTypebot.ts +++ b/apps/builder/src/features/typebot/api/updateTypebot.ts @@ -37,6 +37,7 @@ const typebotUpdateSchemaPick = { whatsAppCredentialsId: true, riskLevel: true, events: true, + updatedAt: true, } as const export const updateTypebot = authenticatedProcedure @@ -67,12 +68,6 @@ export const updateTypebot = authenticatedProcedure title: 'Typebot V5', }), ]), - updatedAt: z - .date() - .optional() - .describe( - 'Used for checking if there is a newer version of the typebot in the database' - ), }) ) .output( @@ -80,143 +75,143 @@ export const updateTypebot = authenticatedProcedure typebot: typebotV6Schema, }) ) - .mutation( - async ({ input: { typebotId, typebot, updatedAt }, ctx: { user } }) => { - const existingTypebot = await prisma.typebot.findFirst({ - where: { - id: typebotId, - }, - select: { - version: true, - id: true, - customDomain: true, - publicId: true, - collaborators: { - select: { - userId: true, - type: true, - }, + .mutation(async ({ input: { typebotId, typebot }, ctx: { user } }) => { + const existingTypebot = await prisma.typebot.findFirst({ + where: { + id: typebotId, + }, + select: { + version: true, + id: true, + customDomain: true, + publicId: true, + collaborators: { + select: { + userId: true, + type: true, }, - workspace: { - select: { - id: true, - plan: true, - isSuspended: true, - isPastDue: true, - members: { - select: { - userId: true, - role: true, - }, + }, + workspace: { + select: { + id: true, + plan: true, + isSuspended: true, + isPastDue: true, + members: { + select: { + userId: true, + role: true, }, }, }, - updatedAt: true, }, + updatedAt: true, + }, + }) + + if ( + !existingTypebot?.id || + (await isWriteTypebotForbidden(existingTypebot, user)) + ) + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Typebot not found', }) - if ( - !existingTypebot?.id || - (await isWriteTypebotForbidden(existingTypebot, user)) - ) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'Typebot not found', - }) + if ( + typebot.updatedAt && + new Date(existingTypebot?.updatedAt).getTime() > + typebot.updatedAt.getTime() + ) + throw new TRPCError({ + code: 'CONFLICT', + message: 'Found newer version of the typebot in database', + }) - if ( - updatedAt && - updatedAt.getTime() > new Date(existingTypebot?.updatedAt).getTime() - ) - throw new TRPCError({ - code: 'CONFLICT', - message: 'Found newer version of the typebot in database', - }) + if ( + typebot.customDomain && + existingTypebot.customDomain !== typebot.customDomain && + (await isCustomDomainNotAvailable(typebot.customDomain)) + ) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Custom domain not available', + }) + if (typebot.publicId) { + if (isCloudProdInstance() && typebot.publicId.length < 4) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'Public id should be at least 4 characters long', + }) if ( - typebot.customDomain && - existingTypebot.customDomain !== typebot.customDomain && - (await isCustomDomainNotAvailable(typebot.customDomain)) + existingTypebot.publicId !== typebot.publicId && + (await isPublicIdNotAvailable(typebot.publicId)) ) throw new TRPCError({ code: 'BAD_REQUEST', - message: 'Custom domain not available', + message: 'Public id not available', }) - - if (typebot.publicId) { - if (isCloudProdInstance() && typebot.publicId.length < 4) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Public id should be at least 4 characters long', - }) - if ( - existingTypebot.publicId !== typebot.publicId && - (await isPublicIdNotAvailable(typebot.publicId)) - ) - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'Public id not available', - }) - } - - if ( - typebot.settings?.whatsApp?.isEnabled && - !hasProPerks(existingTypebot.workspace) - ) { - throw new TRPCError({ - code: 'BAD_REQUEST', - message: 'WhatsApp can be enabled only on a Pro workspaces', - }) - } - - const newTypebot = await prisma.typebot.update({ - where: { - id: existingTypebot.id, - }, - data: { - version: typebot.version ?? undefined, - name: typebot.name, - icon: typebot.icon, - selectedThemeTemplateId: typebot.selectedThemeTemplateId, - events: typebot.events ?? undefined, - groups: typebot.groups - ? await sanitizeGroups(existingTypebot.workspace.id)(typebot.groups) - : undefined, - theme: typebot.theme ? typebot.theme : undefined, - settings: typebot.settings - ? sanitizeSettings( - typebot.settings, - existingTypebot.workspace.plan, - 'update' - ) - : undefined, - folderId: typebot.folderId, - variables: typebot.variables, - edges: typebot.edges, - resultsTablePreferences: - typebot.resultsTablePreferences === null - ? Prisma.DbNull - : typebot.resultsTablePreferences, - publicId: - typebot.publicId === null - ? null - : typebot.publicId && isPublicIdValid(typebot.publicId) - ? typebot.publicId - : undefined, - customDomain: - typebot.customDomain === null ? null : typebot.customDomain, - isClosed: typebot.isClosed, - whatsAppCredentialsId: typebot.whatsAppCredentialsId ?? undefined, - }, - }) - - const migratedTypebot = await migrateTypebot( - typebotSchema.parse(newTypebot) - ) - - return { typebot: migratedTypebot } } - ) + + if ( + typebot.settings?.whatsApp?.isEnabled && + !hasProPerks(existingTypebot.workspace) + ) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'WhatsApp can be enabled only on a Pro workspaces', + }) + } + + const newTypebot = await prisma.typebot.update({ + where: { + id: existingTypebot.id, + }, + data: { + version: typebot.version ?? undefined, + name: typebot.name, + icon: typebot.icon, + selectedThemeTemplateId: typebot.selectedThemeTemplateId, + events: typebot.events ?? undefined, + groups: typebot.groups + ? await sanitizeGroups(existingTypebot.workspace.id)(typebot.groups) + : undefined, + theme: typebot.theme ? typebot.theme : undefined, + settings: typebot.settings + ? sanitizeSettings( + typebot.settings, + existingTypebot.workspace.plan, + 'update' + ) + : undefined, + folderId: typebot.folderId, + variables: typebot.variables, + edges: typebot.edges, + resultsTablePreferences: + typebot.resultsTablePreferences === null + ? Prisma.DbNull + : typebot.resultsTablePreferences, + publicId: + typebot.publicId === null + ? null + : typebot.publicId && isPublicIdValid(typebot.publicId) + ? typebot.publicId + : undefined, + customDomain: + typebot.customDomain === null ? null : typebot.customDomain, + isClosed: typebot.isClosed, + whatsAppCredentialsId: typebot.whatsAppCredentialsId ?? undefined, + updatedAt: typebot.updatedAt, + }, + }) + + const migratedTypebot = await migrateTypebot( + typebotSchema.parse(newTypebot) + ) + + return { typebot: migratedTypebot } + }) const isPublicIdValid = (str: string) => /^([a-z0-9]+-[a-z0-9]*)*$/.test(str) || /^[a-z0-9]*$/.test(str) diff --git a/apps/docs/openapi/builder.json b/apps/docs/openapi/builder.json index ca5226a40..0bb80f40d 100644 --- a/apps/docs/openapi/builder.json +++ b/apps/docs/openapi/builder.json @@ -4401,6 +4401,9 @@ } ] } + }, + "updatedAt": { + "type": "string" } }, "title": "Typebot V6" @@ -7003,15 +7006,14 @@ }, "events": { "type": "array" + }, + "updatedAt": { + "type": "string" } }, "title": "Typebot V5" } ] - }, - "updatedAt": { - "type": "string", - "description": "Used for checking if there is a newer version of the typebot in the database" } }, "required": [