diff --git a/server/src/controllers/apps.controller.v2.ts b/server/src/controllers/apps.controller.v2.ts index 1a592a7a64..ab2d72511a 100644 --- a/server/src/controllers/apps.controller.v2.ts +++ b/server/src/controllers/apps.controller.v2.ts @@ -188,7 +188,7 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - await this.componentsService.update(versionEditDto.diff); + await this.componentsService.update(versionEditDto.diff, versionId); } @UseGuards(JwtAuthGuard) @@ -212,7 +212,7 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - await this.componentsService.delete(versionEditDto.diff); + await this.componentsService.delete(versionEditDto.diff, versionId); } @UseGuards(JwtAuthGuard) @@ -236,7 +236,7 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - await this.componentsService.componentLayoutChange(versionEditDto.diff); + await this.componentsService.componentLayoutChange(versionEditDto.diff, versionId); } // pages api @@ -300,7 +300,7 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - await this.pageService.updatePage({ pageId: updatePageDto.pageId, diff: updatePageDto.diff }); + await this.pageService.updatePage({ pageId: updatePageDto.pageId, diff: updatePageDto.diff }, versionId); } @UseGuards(JwtAuthGuard) @@ -363,7 +363,7 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - return await this.eventService.updateEvent(body?.events, body?.updateType); + return await this.eventService.updateEvent(body?.events, body?.updateType, versionId); } @UseGuards(JwtAuthGuard) @@ -382,6 +382,6 @@ export class AppsControllerV2 { throw new ForbiddenException('You do not have permissions to perform this action'); } - return await this.eventService.deleteEvent(eventId); + return await this.eventService.deleteEvent(eventId, versionId); } } diff --git a/server/src/helpers/utils.helper.ts b/server/src/helpers/utils.helper.ts index 3d62db24cf..cd5afd96fe 100644 --- a/server/src/helpers/utils.helper.ts +++ b/server/src/helpers/utils.helper.ts @@ -80,6 +80,26 @@ export async function dbTransactionWrap(operation: (...args) => any, manager?: E } } +export const updateTimestampForAppVersion = async (manager, appVersionId) => { + const appVersion = await manager.findOne('app_versions', appVersionId); + if (appVersion) { + await manager.update('app_versions', appVersionId, { updatedAt: new Date() }); + } +}; + +export async function dbTransactionForAppVersionAssociationsUpdate( + operation: (...args) => any, + appVersionId: string +): Promise { + return await getManager().transaction(async (manager) => { + const result = await operation(manager); + + await updateTimestampForAppVersion(manager, appVersionId); + + return result; + }); +} + export const defaultAppEnvironments = [{ name: 'production', isDefault: true, priority: 3 }]; export async function catchDbException( operation: () => any, diff --git a/server/src/services/components.service.ts b/server/src/services/components.service.ts index 1650ffe17d..240531059d 100644 --- a/server/src/services/components.service.ts +++ b/server/src/services/components.service.ts @@ -4,7 +4,7 @@ import { EntityManager, Repository } from 'typeorm'; import { Component } from 'src/entities/component.entity'; import { Layout } from 'src/entities/layout.entity'; import { Page } from 'src/entities/page.entity'; -import { dbTransactionWrap } from 'src/helpers/utils.helper'; +import { dbTransactionForAppVersionAssociationsUpdate, dbTransactionWrap } from 'src/helpers/utils.helper'; import { EventsService } from './events_handler.service'; @@ -22,7 +22,7 @@ export class ComponentsService { } async create(componentDiff: object, pageId: string, appVersionId: string) { - return dbTransactionWrap(async (manager: EntityManager) => { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { const page = await manager.findOne(Page, { where: { appVersionId, id: pageId }, }); @@ -58,11 +58,11 @@ export class ComponentsService { await manager.save(Layout, componentLayouts); return {}; - }); + }, appVersionId); } - async update(componentDiff: object) { - return dbTransactionWrap(async (manager) => { + async update(componentDiff: object, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { for (const componentId in componentDiff) { const { component } = componentDiff[componentId]; @@ -105,11 +105,11 @@ export class ComponentsService { return; } - }); + }, appVersionId); } - async delete(componentIds: string[]) { - return dbTransactionWrap(async (manager: EntityManager) => { + async delete(componentIds: string[], appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { const components = await manager.findByIds(Component, componentIds); if (!components.length) { @@ -125,11 +125,11 @@ export class ComponentsService { }); await manager.delete(Component, componentIds); - }); + }, appVersionId); } - async componentLayoutChange(componenstLayoutDiff: object) { - return dbTransactionWrap(async (manager: EntityManager) => { + async componentLayoutChange(componenstLayoutDiff: object, appVersionId: string) { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { for (const componentId in componenstLayoutDiff) { const doesComponentExist = await manager.findAndCount(Component, { id: componentId }); @@ -155,7 +155,7 @@ export class ComponentsService { } } } - }); + }, appVersionId); } async getAllComponents(pageId: string) { diff --git a/server/src/services/events_handler.service.ts b/server/src/services/events_handler.service.ts index efcfa6be3a..08314e40f9 100644 --- a/server/src/services/events_handler.service.ts +++ b/server/src/services/events_handler.service.ts @@ -4,7 +4,7 @@ import { EntityManager, Repository } from 'typeorm'; import { Component } from 'src/entities/component.entity'; import { EventHandler } from 'src/entities/event_handler.entity'; -import { dbTransactionWrap } from 'src/helpers/utils.helper'; +import { dbTransactionWrap, dbTransactionForAppVersionAssociationsUpdate } from 'src/helpers/utils.helper'; @Injectable() export class EventsService { @@ -55,14 +55,14 @@ export class EventsService { index: eventObj.index, }; - return await dbTransactionWrap(async (manager: EntityManager) => { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { const event = await manager.save(EventHandler, newEvent); return event; - }); + }, versionId); } - async updateEvent(events: [], updateType: 'update' | 'reorder') { - return await dbTransactionWrap(async (manager: EntityManager) => { + async updateEvent(events: [], updateType: 'update' | 'reorder', appVersionId: string) { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { return await Promise.all( events.map(async (event) => { const { event_id, diff } = event as any; @@ -91,7 +91,7 @@ export class EventsService { return await manager.save(EventHandler, updatedEvent); }) ); - }); + }, appVersionId); } async updateEventsOrderOnDelete(sourceId: string, deletedIndex: number) { @@ -112,8 +112,8 @@ export class EventsService { }); } - async deleteEvent(eventId: string) { - return await dbTransactionWrap(async (manager: EntityManager) => { + async deleteEvent(eventId: string, appVersionId: string) { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { const event = await manager.findOne(EventHandler, { where: { id: eventId }, }); @@ -129,6 +129,6 @@ export class EventsService { } await this.updateEventsOrderOnDelete(event.sourceId, event.index); return deleteResponse; - }); + }, appVersionId); } } diff --git a/server/src/services/page.service.ts b/server/src/services/page.service.ts index a28a8f1f57..c4689fbfa1 100644 --- a/server/src/services/page.service.ts +++ b/server/src/services/page.service.ts @@ -6,7 +6,7 @@ import { Page } from 'src/entities/page.entity'; import { ComponentsService } from './components.service'; import { CreatePageDto, UpdatePageDto } from '@dto/pages.dto'; import { AppsService } from './apps.service'; -import { dbTransactionWrap } from 'src/helpers/utils.helper'; +import { dbTransactionWrap, dbTransactionForAppVersionAssociationsUpdate } from 'src/helpers/utils.helper'; import { EventsService } from './events_handler.service'; import { Component } from 'src/entities/component.entity'; import { Layout } from 'src/entities/layout.entity'; @@ -42,54 +42,58 @@ export class PageService { } async createPage(page: CreatePageDto, appVersionId: string): Promise { - const newPage = new Page(); - newPage.id = page.id; - newPage.name = page.name; - newPage.handle = page.handle; - newPage.index = page.index; - newPage.appVersionId = appVersionId; + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { + const newPage = new Page(); + newPage.id = page.id; + newPage.name = page.name; + newPage.handle = page.handle; + newPage.index = page.index; + newPage.appVersionId = appVersionId; - return this.pageRepository.save(newPage); + return await manager.save(Page, newPage); + }, appVersionId); } async clonePage(pageId: string, appVersionId: string) { - const pageToClone = await this.pageRepository.findOne(pageId); + return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { + const pageToClone = await manager.findOne(Page, pageId); - if (!pageToClone) { - throw new Error('Page not found'); - } + if (!pageToClone) { + throw new Error('Page not found'); + } - let pageName = `${pageToClone.name} (copy)`; - let pageHandle = `${pageToClone.handle}-copy`; + let pageName = `${pageToClone.name} (copy)`; + let pageHandle = `${pageToClone.handle}-copy`; - const allPages = await this.pageRepository.find({ appVersionId }); + const allPages = await this.pageRepository.find({ appVersionId }); - const pageNameORHandleExists = allPages.filter((page) => { - return page.name.includes(pageName) || page.handle.includes(pageHandle); - }); + const pageNameORHandleExists = allPages.filter((page) => { + return page.name.includes(pageName) || page.handle.includes(pageHandle); + }); - if (pageNameORHandleExists.length > 0) { - pageName = `${pageToClone.name} (copy ${pageNameORHandleExists.length})`; - pageHandle = `${pageToClone.handle}-copy-${pageNameORHandleExists.length}`; - } + if (pageNameORHandleExists.length > 0) { + pageName = `${pageToClone.name} (copy ${pageNameORHandleExists.length})`; + pageHandle = `${pageToClone.handle}-copy-${pageNameORHandleExists.length}`; + } - const newPage = new Page(); - newPage.name = pageName; - newPage.handle = pageHandle; - newPage.index = pageToClone.index + 1; - newPage.appVersionId = appVersionId; + const newPage = new Page(); + newPage.name = pageName; + newPage.handle = pageHandle; + newPage.index = pageToClone.index + 1; + newPage.appVersionId = appVersionId; - const clonedpage = await this.pageRepository.save(newPage); + const clonedpage = await this.pageRepository.save(newPage); - await this.clonePageEventsAndComponents(pageId, clonedpage.id, appVersionId); + await this.clonePageEventsAndComponents(pageId, clonedpage.id); - const pages = await this.findPagesForVersion(appVersionId); - const events = await this.eventHandlerService.findEventsForVersion(appVersionId); + const pages = await this.findPagesForVersion(appVersionId); + const events = await this.eventHandlerService.findEventsForVersion(appVersionId); - return { pages, events }; + return { pages, events }; + }, appVersionId); } - async clonePageEventsAndComponents(pageId: string, clonePageId: string, appVersionId: string) { + async clonePageEventsAndComponents(pageId: string, clonePageId: string) { return dbTransactionWrap(async (manager: EntityManager) => { const pageComponents = await manager.find(Component, { pageId }); const pageEvents = await this.eventHandlerService.findAllEventsWithSourceId(pageId); @@ -98,6 +102,14 @@ export class PageService { // Clone events await Promise.all( pageEvents.map(async (event) => { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = componentsIdMap[eventDefinition.componentId]; + } + + event.event = eventDefinition; + const clonedEvent = { ...event, id: undefined, sourceId: clonePageId }; await manager.save(EventHandler, clonedEvent); }) @@ -119,11 +131,20 @@ export class PageService { // Clone component events const clonedComponentEvents = await this.eventHandlerService.findAllEventsWithSourceId(component.id); - const clonedEvents = clonedComponentEvents.map((event) => ({ - ...event, - id: undefined, - sourceId: newComponent.id, - })); + const clonedEvents = clonedComponentEvents.map((event) => { + const eventDefinition = event.event; + + if (eventDefinition?.actionId === 'control-component') { + eventDefinition.componentId = componentsIdMap[eventDefinition.componentId]; + } + event.event = eventDefinition; + + return { + ...event, + id: undefined, + sourceId: newComponent.id, + }; + }); await manager.save(Layout, clonedLayouts); await manager.save(EventHandler, clonedEvents); @@ -141,9 +162,9 @@ export class PageService { }); } - async updatePage(pageUpdates: UpdatePageDto) { + async updatePage(pageUpdates: UpdatePageDto, appVersionId: string) { if (Object.keys(pageUpdates.diff).length > 1) { - return this.updatePagesOrder(pageUpdates.diff); + return this.updatePagesOrder(pageUpdates.diff, appVersionId); } const currentPage = await this.pageRepository.findOne(pageUpdates.pageId); @@ -154,7 +175,7 @@ export class PageService { return this.pageRepository.update(pageUpdates.pageId, pageUpdates.diff); } - async updatePagesOrder(pages) { + async updatePagesOrder(pages, appVersionId: string) { const pagesToPage = Object.keys(pages).map((pageId) => { return { id: pageId, @@ -162,18 +183,18 @@ export class PageService { }; }); - return await dbTransactionWrap(async (manager: EntityManager) => { + return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { await Promise.all( pagesToPage.map(async (page) => { await manager.update(Page, page.id, page); }) ); - }); + }, appVersionId); } async deletePage(pageId: string, appVersionId: string) { const { editingVersion } = await this.appService.findAppFromVersion(appVersionId); - return dbTransactionWrap(async (manager: EntityManager) => { + return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => { const pageExists = await manager.findOne(Page, pageId); if (!pageExists) { @@ -200,7 +221,7 @@ export class PageService { await manager.update(Page, page.id, page); }) ); - }); + }, appVersionId); } rearrangePagesOnDelete(pages: Page[], pageDeletedIndex: number) {