Metadata api create entity in workspace custom app (#15911)
# Introduction Cleaner and fewer scope version of https://github.com/twentyhq/twenty/pull/15745 ( removed sync-metadata hack through, too ambitious migration and upgrade ) Please note that this PR won't have any interaction with the existing sync-metadata Which mean that the sync metadata does not update the standard entities applicationId and universalIdentifier, and it won't we will deprecate it on favor of a workspace migration aka twenty-standard app installation ## API Metadata Any operation going through the api metadata nows automatically scope the related entity to the workspace custom application instance. ( optionally passing an applicationId to allow current hacky implem of app sync service ) We need to either ignore the tests or remove the cli status check from the blocking status badges for a PR to be merged ## New workspace Already handled in previous https://github.com/twentyhq/twenty/pull/15625, when a workspace is created it gets created a twenty standard and custom workspace instance All his views and permissions will be prefilled to the its twenty standard app instance with a specific universalIdentifier ## New universalIdentifier At the contrary as before with standardIds, universalIdentifier are unique for a given workspace This means that createdAt field of both object company and opportunity will have a unique universalIdentifier whereas they share the same standardId ## FlatApplication Introduced the flatApplication and cache. Will migrate existing `MetadataName` to be `SyncableMetadataName` in a following PR ## What's next Next we will describe a twenty standard app configuration as json that will be used to generate a workspace migration that will be run instead of the sync metadata, in a nutshell we aim to deprecated the sync metadata So we can standardize any entity to have a non nullable applicationId and universalIdentifier ## Upgrade command Introduced an upgrade command that will create a custom workspace instance for any workspace that do not have one in order to align with the new behavior when creating a new workspace
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApplicationResolver } from 'src/engine/core-modules/application/application.resolver';
|
||||
import { ApplicationSyncService } from 'src/engine/core-modules/application/application-sync.service';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { ApplicationVariableEntityModule } from 'src/engine/core-modules/applicationVariable/application-variable.module';
|
||||
import { CronTriggerModule } from 'src/engine/metadata-modules/cron-trigger/cron-trigger.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { DatabaseEventTriggerModule } from 'src/engine/metadata-modules/database-event-trigger/database-event-trigger.module';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RouteTriggerModule } from 'src/engine/metadata-modules/route-trigger/route-trigger.module';
|
||||
import { ServerlessFunctionLayerModule } from 'src/engine/metadata-modules/serverless-function-layer/serverless-function-layer.module';
|
||||
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ApplicationModule,
|
||||
ApplicationVariableEntityModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
ObjectMetadataModule,
|
||||
FieldMetadataModule,
|
||||
DataSourceModule,
|
||||
ServerlessFunctionLayerModule,
|
||||
ServerlessFunctionModule,
|
||||
DatabaseEventTriggerModule,
|
||||
CronTriggerModule,
|
||||
RouteTriggerModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [ApplicationResolver, ApplicationSyncService],
|
||||
exports: [ApplicationSyncService],
|
||||
})
|
||||
export class ApplicationSyncModule {}
|
||||
+17
@@ -284,6 +284,7 @@ export class ApplicationSyncService {
|
||||
await this.fieldMetadataServiceV2.createOneField({
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -406,6 +407,7 @@ export class ApplicationSyncService {
|
||||
|
||||
const createdObject = await this.objectMetadataServiceV2.createOneObject({
|
||||
createObjectInput,
|
||||
applicationId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
@@ -526,18 +528,21 @@ export class ApplicationSyncService {
|
||||
serverlessFunctionId: serverlessFunctionToUpdate.id,
|
||||
triggersToSync: serverlessFunctionToSync.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
await this.syncCronTriggersForServerlessFunction({
|
||||
serverlessFunctionId: serverlessFunctionToUpdate.id,
|
||||
triggersToSync: serverlessFunctionToSync.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
await this.syncRouteTriggersForServerlessFunction({
|
||||
serverlessFunctionId: serverlessFunctionToUpdate.id,
|
||||
triggersToSync: serverlessFunctionToSync.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -567,18 +572,21 @@ export class ApplicationSyncService {
|
||||
serverlessFunctionId: createdServerlessFunction.id,
|
||||
triggersToSync: serverlessFunctionToCreate.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
await this.syncCronTriggersForServerlessFunction({
|
||||
serverlessFunctionId: createdServerlessFunction.id,
|
||||
triggersToSync: serverlessFunctionToCreate.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
await this.syncRouteTriggersForServerlessFunction({
|
||||
serverlessFunctionId: createdServerlessFunction.id,
|
||||
triggersToSync: serverlessFunctionToCreate.triggers || [],
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -587,10 +595,12 @@ export class ApplicationSyncService {
|
||||
serverlessFunctionId,
|
||||
triggersToSync,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
}: {
|
||||
serverlessFunctionId: string;
|
||||
triggersToSync: ServerlessFunctionTriggerManifest[];
|
||||
workspaceId: string;
|
||||
applicationId: string;
|
||||
}) {
|
||||
const databaseEventTriggersToSync = triggersToSync.filter(
|
||||
(trigger) => trigger.type === 'databaseEvent',
|
||||
@@ -695,6 +705,7 @@ export class ApplicationSyncService {
|
||||
await this.databaseEventTriggerV2Service.createOne(
|
||||
createDatabaseEventTriggerInput,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -703,10 +714,12 @@ export class ApplicationSyncService {
|
||||
serverlessFunctionId,
|
||||
triggersToSync,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
}: {
|
||||
serverlessFunctionId: string;
|
||||
triggersToSync: ServerlessFunctionTriggerManifest[];
|
||||
workspaceId: string;
|
||||
applicationId: string;
|
||||
}) {
|
||||
const cronTriggersToSync = triggersToSync.filter(
|
||||
(trigger) => trigger.type === 'cron',
|
||||
@@ -808,6 +821,7 @@ export class ApplicationSyncService {
|
||||
await this.cronTriggerV2Service.createOne(
|
||||
createCronTriggerInput,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -816,10 +830,12 @@ export class ApplicationSyncService {
|
||||
serverlessFunctionId,
|
||||
triggersToSync,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
}: {
|
||||
serverlessFunctionId: string;
|
||||
triggersToSync: ServerlessFunctionTriggerManifest[];
|
||||
workspaceId: string;
|
||||
applicationId: string;
|
||||
}) {
|
||||
const routeTriggersToSync = triggersToSync.filter(
|
||||
(trigger) => trigger.type === 'route',
|
||||
@@ -922,6 +938,7 @@ export class ApplicationSyncService {
|
||||
await this.routeTriggerV2Service.createOne(
|
||||
createRouteTriggerInput,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,19 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationSyncService } from 'src/engine/core-modules/application/application-sync.service';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationResolver } from 'src/engine/core-modules/application/application.resolver';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { ApplicationVariableEntityModule } from 'src/engine/core-modules/applicationVariable/application-variable.module';
|
||||
import { WorkspaceFlatApplicationMapCacheService } from 'src/engine/core-modules/application/services/workspace-flat-application-map-cache.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
|
||||
import { CronTriggerModule } from 'src/engine/metadata-modules/cron-trigger/cron-trigger.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { DatabaseEventTriggerModule } from 'src/engine/metadata-modules/database-event-trigger/database-event-trigger.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RouteTriggerModule } from 'src/engine/metadata-modules/route-trigger/route-trigger.module';
|
||||
import { ServerlessFunctionLayerModule } from 'src/engine/metadata-modules/serverless-function-layer/serverless-function-layer.module';
|
||||
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ApplicationEntity, AgentEntity, WorkspaceEntity]),
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
ObjectMetadataModule,
|
||||
FieldMetadataModule,
|
||||
DataSourceModule,
|
||||
ApplicationVariableEntityModule,
|
||||
ServerlessFunctionLayerModule,
|
||||
ServerlessFunctionModule,
|
||||
DatabaseEventTriggerModule,
|
||||
CronTriggerModule,
|
||||
RouteTriggerModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
PermissionsModule,
|
||||
],
|
||||
exports: [ApplicationService],
|
||||
providers: [ApplicationResolver, ApplicationService, ApplicationSyncService],
|
||||
exports: [ApplicationService, WorkspaceFlatApplicationMapCacheService],
|
||||
providers: [ApplicationService, WorkspaceFlatApplicationMapCacheService],
|
||||
})
|
||||
export class ApplicationModule {}
|
||||
|
||||
+125
-14
@@ -3,23 +3,86 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryRunner, Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import {
|
||||
ApplicationException,
|
||||
ApplicationExceptionCode,
|
||||
} from 'src/engine/core-modules/application/application.exception';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/core-modules/application/constants/twenty-standard-applications';
|
||||
import { WorkspaceFlatApplicationMapCacheService } from 'src/engine/core-modules/application/services/workspace-flat-application-map-cache.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/twenty-standard-applications';
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationService {
|
||||
constructor(
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceFlatApplicationMapCacheService: WorkspaceFlatApplicationMapCacheService,
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
) {}
|
||||
|
||||
async findWorkspaceTwentyStandardAndCustomApplicationOrThrow({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: {
|
||||
id: workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(workspace)) {
|
||||
throw new ApplicationException(
|
||||
`Could not find workspace ${workspaceId}`,
|
||||
ApplicationExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const flatApplicationMaps =
|
||||
await this.workspaceFlatApplicationMapCacheService.getExistingOrRecomputeFlatMaps(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
const twentyStandardApplicationId =
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier
|
||||
];
|
||||
|
||||
if (!isDefined(twentyStandardApplicationId)) {
|
||||
throw new ApplicationException(
|
||||
`Could not find workspace twenty standard applicationId in cache ${workspaceId}`,
|
||||
ApplicationExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const twentyStandardFlatApplication =
|
||||
flatApplicationMaps.byId[twentyStandardApplicationId];
|
||||
const workspaceCustomFlatApplication =
|
||||
flatApplicationMaps.byId[workspace.workspaceCustomApplicationId];
|
||||
|
||||
if (
|
||||
!isDefined(twentyStandardFlatApplication) ||
|
||||
!isDefined(workspaceCustomFlatApplication)
|
||||
) {
|
||||
throw new ApplicationException(
|
||||
`Could not find workspace custom and standard applications ${workspace.id}`,
|
||||
ApplicationExceptionCode.APPLICATION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
twentyStandardFlatApplication,
|
||||
workspaceCustomFlatApplication,
|
||||
};
|
||||
}
|
||||
|
||||
async findManyApplications(
|
||||
workspaceId: string,
|
||||
): Promise<ApplicationEntity[]> {
|
||||
@@ -82,31 +145,63 @@ export class ApplicationService {
|
||||
async createTwentyStandardApplication(
|
||||
{
|
||||
workspaceId,
|
||||
skipCacheInvalidation = false,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
skipCacheInvalidation?: boolean;
|
||||
},
|
||||
queryRunner?: QueryRunner,
|
||||
) {
|
||||
return await this.create(
|
||||
const twentyStandardApplication = await this.create(
|
||||
{
|
||||
...TWENTY_STANDARD_APPLICATION,
|
||||
serverlessFunctionLayerId: null,
|
||||
workspaceId,
|
||||
canBeUninstalled: false,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
if (!skipCacheInvalidation) {
|
||||
await this.workspaceFlatApplicationMapCacheService.invalidateCache({
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
return twentyStandardApplication;
|
||||
}
|
||||
|
||||
async createWorkspaceCustomApplication(
|
||||
{
|
||||
workspaceId,
|
||||
workspaceDisplayName,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
workspaceDisplayName?: string;
|
||||
},
|
||||
queryRunner?: QueryRunner,
|
||||
) {
|
||||
const applicationId = v4();
|
||||
const workspaceCustomApplication = await this.create(
|
||||
{
|
||||
description: 'Workspace custom application',
|
||||
name: `${isDefined(workspaceDisplayName) ? workspaceDisplayName : 'Workspace'}'s custom application`,
|
||||
sourcePath: 'workspace-custom',
|
||||
version: '1.0.0',
|
||||
universalIdentifier: applicationId,
|
||||
workspaceId: workspaceId,
|
||||
id: applicationId,
|
||||
serverlessFunctionLayerId: null,
|
||||
canBeUninstalled: false,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
return workspaceCustomApplication;
|
||||
}
|
||||
|
||||
async create(
|
||||
data: {
|
||||
universalIdentifier?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
serverlessFunctionLayerId: string | null;
|
||||
sourcePath: string;
|
||||
workspaceId: string;
|
||||
},
|
||||
data: Partial<ApplicationEntity> & { workspaceId: string },
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<ApplicationEntity> {
|
||||
const application = this.applicationRepository.create({
|
||||
@@ -118,7 +213,13 @@ export class ApplicationService {
|
||||
return queryRunner.manager.save(ApplicationEntity, application);
|
||||
}
|
||||
|
||||
return this.applicationRepository.save(application);
|
||||
const savedApplication = await this.applicationRepository.save(application);
|
||||
|
||||
await this.workspaceFlatApplicationMapCacheService.invalidateCache({
|
||||
workspaceId: data.workspaceId,
|
||||
});
|
||||
|
||||
return savedApplication;
|
||||
}
|
||||
|
||||
async update(
|
||||
@@ -127,6 +228,10 @@ export class ApplicationService {
|
||||
): Promise<ApplicationEntity> {
|
||||
await this.applicationRepository.update({ id }, data);
|
||||
|
||||
await this.workspaceFlatApplicationMapCacheService.invalidateCache({
|
||||
workspaceId: data.workspaceId as string,
|
||||
});
|
||||
|
||||
const updatedApplication = await this.findById(id);
|
||||
|
||||
if (!updatedApplication) {
|
||||
@@ -151,8 +256,14 @@ export class ApplicationService {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({
|
||||
await this.workspaceFlatApplicationMapCacheService.invalidateCache({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.invalidateFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
|
||||
export const APPLICATION_ENTITY_RELATION_PROPERTIES = [
|
||||
'workspace',
|
||||
'agents',
|
||||
'serverlessFunctions',
|
||||
'objects',
|
||||
'applicationVariables',
|
||||
] as const satisfies (keyof ApplicationEntity)[];
|
||||
+1919
File diff suppressed because it is too large
Load Diff
+19
@@ -0,0 +1,19 @@
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
|
||||
export const TWENTY_STANDARD_APPLICATION = {
|
||||
universalIdentifier: '20202020-64aa-4b6f-b003-9c74b97cee20',
|
||||
name: 'Twenty Standard',
|
||||
description:
|
||||
'Twenty is an open-source CRM that allows you to manage your sales and customer relationships',
|
||||
version: '1.0.0',
|
||||
sourcePath: 'cli-sync',
|
||||
sourceType: 'local',
|
||||
} as const satisfies Pick<
|
||||
ApplicationEntity,
|
||||
| 'universalIdentifier'
|
||||
| 'name'
|
||||
| 'description'
|
||||
| 'version'
|
||||
| 'sourcePath'
|
||||
| 'sourceType'
|
||||
>;
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { FlatApplicationCacheMaps } from 'src/engine/core-modules/application/types/flat-application-cache-maps.type';
|
||||
import { fromApplicationEntityToFlatApplication } from 'src/engine/core-modules/application/utils/from-application-entity-to-flat-application.util';
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache(
|
||||
'flatApplicationMaps' as MetadataToFlatEntityMapsKey<AllMetadataName>,
|
||||
) // TODO prastoin introduce SyncableMetadata notion
|
||||
export class WorkspaceFlatApplicationMapCacheService extends WorkspaceFlatMapCacheService<FlatApplicationCacheMaps> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatApplicationCacheMaps> {
|
||||
const applicationEntities = await this.applicationRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
const flatApplicationMaps: FlatApplicationCacheMaps = {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
};
|
||||
|
||||
for (const applicationEntity of applicationEntities) {
|
||||
const flatApplication =
|
||||
fromApplicationEntityToFlatApplication(applicationEntity);
|
||||
|
||||
flatApplicationMaps.byId[flatApplication.id] = flatApplication;
|
||||
flatApplicationMaps.idByUniversalIdentifier[
|
||||
flatApplication.universalIdentifier
|
||||
] = flatApplication.id;
|
||||
}
|
||||
|
||||
return flatApplicationMaps;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type APPLICATION_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/application/constants/application-entity-relation-properties.constant';
|
||||
|
||||
export type ApplicationEntityRelationProperties =
|
||||
(typeof APPLICATION_ENTITY_RELATION_PROPERTIES)[number];
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
|
||||
export type FlatApplicationCacheMaps = {
|
||||
byId: Partial<Record<string, FlatApplication>>;
|
||||
idByUniversalIdentifier: Partial<Record<string, string>>;
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { type ApplicationEntityRelationProperties } from 'src/engine/core-modules/application/types/application-entity-relation-properties.type';
|
||||
|
||||
export type FlatApplication = Omit<
|
||||
ApplicationEntity,
|
||||
ApplicationEntityRelationProperties
|
||||
>;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { APPLICATION_ENTITY_RELATION_PROPERTIES } from 'src/engine/core-modules/application/constants/application-entity-relation-properties.constant';
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
|
||||
export const fromApplicationEntityToFlatApplication = (
|
||||
applicationEntity: ApplicationEntity,
|
||||
): FlatApplication =>
|
||||
removePropertiesFromRecord(
|
||||
applicationEntity,
|
||||
APPLICATION_ENTITY_RELATION_PROPERTIES,
|
||||
);
|
||||
Reference in New Issue
Block a user