From 2c38b853681f7320fce8670cd342b20e07aceb32 Mon Sep 17 00:00:00 2001 From: arpitnath Date: Wed, 27 Mar 2024 02:11:58 +0530 Subject: [PATCH] handles references mapping with new associations --- frontend/src/Editor/Editor.jsx | 30 ++----- frontend/src/Editor/Viewer.jsx | 30 ++----- frontend/src/_stores/appDataStore.js | 10 +-- server/src/helpers/import_export.helpers.ts | 53 ++++++++++++ .../src/services/app_import_export.service.ts | 84 ++++++++++++++----- 5 files changed, 138 insertions(+), 69 deletions(-) create mode 100644 server/src/helpers/import_export.helpers.ts diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 339ae289eb..80102bf106 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -782,29 +782,17 @@ const EditorComponent = (props) => { let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries)); let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events)); - const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); - const entityReferencesInQueryoOptions = findAllEntityReferences(dataQueries, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInQueryoOptions = findAllEntityReferences(dataQueries, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); - const entityReferencesInEvents = findAllEntityReferences(allEvents, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); const manager = useResolveStore.getState().referenceMapper; diff --git a/frontend/src/Editor/Viewer.jsx b/frontend/src/Editor/Viewer.jsx index 5fe4328056..e210a0846e 100644 --- a/frontend/src/Editor/Viewer.jsx +++ b/frontend/src/Editor/Viewer.jsx @@ -214,29 +214,17 @@ class ViewerComponent extends React.Component { let allDataQueries = this.state.dataQueries || []; let newComponentDefinition = JSON.parse(JSON.stringify(components)); - const entityReferencesInComponentDefinitions = findAllEntityReferences(newComponentDefinition, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInComponentDefinitions = findAllEntityReferences(newComponentDefinition, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); - const entityReferencesInQueryoOptions = findAllEntityReferences(allDataQueries, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInQueryoOptions = findAllEntityReferences(allDataQueries, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); - const entityReferencesInEvents = findAllEntityReferences(allEvents, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); const manager = useResolveStore.getState().referenceMapper; diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js index a05d8b02d2..2159f6cf84 100644 --- a/frontend/src/_stores/appDataStore.js +++ b/frontend/src/_stores/appDataStore.js @@ -111,13 +111,9 @@ export const useAppDataStore = create( } }); - const entityReferencesInEvents = findAllEntityReferences(updatedEvents, []) - ?.map((entity) => { - if (entity && isValidUUID(entity)) { - return entity; - } - }) - ?.filter((e) => e !== undefined); + const entityReferencesInEvents = findAllEntityReferences(updatedEvents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); const manager = useResolveStore.getState().referenceMapper; let newEvents = JSON.parse(JSON.stringify(updatedEvents)); diff --git a/server/src/helpers/import_export.helpers.ts b/server/src/helpers/import_export.helpers.ts new file mode 100644 index 0000000000..db9b02ddec --- /dev/null +++ b/server/src/helpers/import_export.helpers.ts @@ -0,0 +1,53 @@ +export function updateEntityReferences(node, resourceMapping: Record = {}) { + if (typeof node === 'object') { + for (const key in node) { + let value = node[key]; + if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) { + const referenceExists = value; + + if (referenceExists) { + const ref = value.replace('{{', '').replace('}}', ''); + + const entityName = ref.split('.')[1]; + + if (resourceMapping[entityName]) { + const newValue = value.replace(entityName, resourceMapping[entityName]); + + node[key] = newValue; + } + } + } else if (typeof value === 'object') { + value = updateEntityReferences(value, resourceMapping); + } + } + } + + return node; +} + +export function findAllEntityReferences(node, allRefs): [] { + if (typeof node === 'object') { + for (const key in node) { + const value = node[key]; + if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) { + const referenceExists = value; + + if (referenceExists) { + const ref = value.replace('{{', '').replace('}}', ''); + + const entityName = ref.split('.')[1]; + + allRefs.push(entityName); + } + } else if (typeof value === 'object') { + findAllEntityReferences(value, allRefs); + } + } + } + return allRefs; +} + +export function isValidUUID(uuid) { + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + return uuidRegex.test(uuid); +} diff --git a/server/src/services/app_import_export.service.ts b/server/src/services/app_import_export.service.ts index d30e0f936e..c432c280d7 100644 --- a/server/src/services/app_import_export.service.ts +++ b/server/src/services/app_import_export.service.ts @@ -32,6 +32,7 @@ import { Component } from 'src/entities/component.entity'; import { Layout } from 'src/entities/layout.entity'; import { EventHandler, Target } from 'src/entities/event_handler.entity'; import { v4 as uuid } from 'uuid'; +import { findAllEntityReferences, isValidUUID, updateEntityReferences } from 'src/helpers/import_export.helpers'; interface AppResourceMappings { defaultDataSourceIdMapping: Record; @@ -237,21 +238,20 @@ export class AppImportExportService { ? true : isTooljetVersionWithNormalizedAppDefinitionSchem(importedAppTooljetVersion); - const shouldUpdateForGridCompatibility = !cloning; - const importedApp = await this.createImportedAppForUser(this.entityManager, schemaUnifiedAppParams, user); - await this.setupImportedAppAssociations( + const resourceMapping = await this.setupImportedAppAssociations( this.entityManager, importedApp, schemaUnifiedAppParams, user, externalResourceMappings, isNormalizedAppDefinitionSchema, - shouldUpdateForGridCompatibility, tooljetVersion ); + await this.createAdminGroupPermissions(this.entityManager, importedApp); + await this.updateEntityReferencesForImportedApp(this.entityManager, resourceMapping); // NOTE: App slug updation callback doesn't work while wrapped in transaction // hence updating slug explicitly @@ -262,6 +262,64 @@ export class AppImportExportService { return importedApp; } + async updateEntityReferencesForImportedApp(manager: EntityManager, resourceMapping: AppResourceMappings) { + const mappings = { ...resourceMapping.componentsMapping, ...resourceMapping.dataQueryMapping }; + const newComponentIds = Object.values(resourceMapping.componentsMapping); + const newQueriesIds = Object.values(resourceMapping.dataQueryMapping); + + if (newComponentIds.length > 0) { + const components = await manager + .createQueryBuilder(Component, 'components') + .where('components.id IN(:...componentIds)', { componentIds: newComponentIds }) + .select([ + 'components.id', + 'components.properties', + 'components.styles', + 'components.general', + 'components.validation', + 'components.generalStyles', + 'components.displayPreferences', + ]) + .getMany(); + + const toUpdateComponents = components.filter((component) => { + const entityReferencesInComponentDefinitions = findAllEntityReferences(component, []).filter( + (entity) => entity && isValidUUID(entity) + ); + + if (entityReferencesInComponentDefinitions.length > 0) { + return updateEntityReferences(component, mappings); + } + }); + + if (!isEmpty(toUpdateComponents)) { + await manager.save(toUpdateComponents); + } + } + + if (newQueriesIds.length > 0) { + const dataQueries = await manager + .createQueryBuilder(DataQuery, 'dataQueries') + .where('dataQueries.id IN(:...dataQueryIds)', { dataQueryIds: newQueriesIds }) + .select(['dataQueries.id', 'dataQueries.options']) + .getMany(); + + const toUpdateDataQueries = dataQueries.filter((dataQuery) => { + const entityReferencesInQueryOptions = findAllEntityReferences(dataQuery, []).filter( + (entity) => entity && isValidUUID(entity) + ); + + if (entityReferencesInQueryOptions.length > 0) { + return updateEntityReferences(dataQuery, mappings); + } + }); + + if (!isEmpty(toUpdateDataQueries)) { + await manager.save(toUpdateDataQueries); + } + } + } + async createImportedAppForUser(manager: EntityManager, appParams: any, user: User): Promise { return await catchDbException(async () => { const importedApp = manager.create(App, { @@ -328,7 +386,6 @@ export class AppImportExportService { user: User, externalResourceMappings: Record, isNormalizedAppDefinitionSchema: boolean, - shouldUpdateForGridCompatibility: boolean, tooljetVersion: string ) { // Old version without app version @@ -393,7 +450,6 @@ export class AppImportExportService { importingPages, importingComponents, importingEvents, - shouldUpdateForGridCompatibility, tooljetVersion ); @@ -599,7 +655,6 @@ export class AppImportExportService { importingPages: Page[], importingComponents: Component[], importingEvents: EventHandler[], - shouldUpdateForGridCompatibility: boolean, tooljetVersion: string ): Promise { appResourceMappings = { ...appResourceMappings }; @@ -894,18 +949,6 @@ export class AppImportExportService { ); } - // const appVersionIds = Object.values(appResourceMappings.appVersionMapping); - - // for (const appVersionId of appVersionIds) { - // await this.updateEventActionsForNewVersionWithNewMappingIds( - // manager, - // appVersionId, - // appResourceMappings.dataQueryMapping, - // appResourceMappings.componentsMapping, - // appResourceMappings.pagesMapping - // ); - // } - return appResourceMappings; } @@ -1656,9 +1699,10 @@ export class AppImportExportService { .createQueryBuilder(EventHandler, 'event') .where('event.appVersionId = :versionId', { versionId }) .getMany(); + const mappings = { ...oldDataQueryToNewMapping, ...oldComponentToNewComponentMapping } as Record; for (const event of allEvents) { - const eventDefinition = event.event; + const eventDefinition = updateEntityReferences(event.event, mappings); if (eventDefinition?.actionId === 'run-query' && oldDataQueryToNewMapping[eventDefinition.queryId]) { eventDefinition.queryId = oldDataQueryToNewMapping[eventDefinition.queryId];