Deprecate nullable syncableEntity (#17279)
# Introduction As we've been identifying both standard and custom entities for all the metadata that had standard We now still need to identify all custom entities enforcing them to have an `applicationId` and `universalIdentifier` In this PR we've removed the `SyncableEntityRequired` in favor requiring props directly in the `SyncableEntity` Which means that all metadata in db will now expect non nullable applicationId and universalIdentifier across the whole application Will add some type cleanup later in https://github.com/twentyhq/twenty/pull/17277
This commit is contained in:
+191
@@ -0,0 +1,191 @@
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { DataSource, IsNull, Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ALL_METADATA_ENTITY_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-entity-by-metadata-name.constant';
|
||||
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
|
||||
import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util';
|
||||
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
const REMAINING_ENTITIES_METADATA_NAMES = [
|
||||
'roleTarget',
|
||||
'rowLevelPermissionPredicate',
|
||||
'rowLevelPermissionPredicateGroup',
|
||||
'viewFilterGroup',
|
||||
'cronTrigger',
|
||||
'databaseEventTrigger',
|
||||
'routeTrigger',
|
||||
'serverlessFunction',
|
||||
'skill',
|
||||
'pageLayoutWidget',
|
||||
'pageLayout',
|
||||
'pageLayoutTab',
|
||||
] as const satisfies AllMetadataName[];
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-16:identify-remaining-entities-metadata',
|
||||
description:
|
||||
'Identify remaining entities metadata (roleTarget, rowLevelPermissionPredicate, rowLevelPermissionPredicateGroup, viewFilterGroup, viewSort, cronTrigger, databaseEventTrigger, routeTrigger, serverlessFunction, skill, pageLayoutWidget, pageLayout, pageLayoutTab)',
|
||||
})
|
||||
export class IdentifyRemainingEntitiesMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
protected readonly applicationService: ApplicationService,
|
||||
protected readonly workspaceCacheService: WorkspaceCacheService,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
this.logger.log(
|
||||
`Running identify remaining entities metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
for (const metadataName of REMAINING_ENTITIES_METADATA_NAMES) {
|
||||
await this.identifyEntitiesForMetadataName({
|
||||
metadataName,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
dryRun: options.dryRun ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
await this.identifyViewSortEntities({
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
dryRun: options.dryRun ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
private async identifyEntitiesForMetadataName({
|
||||
metadataName,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
dryRun,
|
||||
}: {
|
||||
metadataName: AllMetadataName;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
dryRun: boolean;
|
||||
}): Promise<void> {
|
||||
const entityClass = ALL_METADATA_ENTITY_BY_METADATA_NAME[metadataName];
|
||||
const repository = this.coreDataSource.getRepository(entityClass);
|
||||
|
||||
const entitiesWithoutApplicationId = await repository.find({
|
||||
select: ['id', 'universalIdentifier', 'applicationId'],
|
||||
where: {
|
||||
workspaceId,
|
||||
applicationId: IsNull(),
|
||||
},
|
||||
});
|
||||
|
||||
if (entitiesWithoutApplicationId.length === 0) {
|
||||
this.logger.log(
|
||||
`No ${metadataName} entities found without applicationId for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const updates = entitiesWithoutApplicationId.map(
|
||||
(entity: SyncableEntity & { id: string }) => ({
|
||||
id: entity.id,
|
||||
universalIdentifier: entity.universalIdentifier ?? v4(),
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
}),
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Found ${updates.length} ${metadataName} entities to update for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (!dryRun) {
|
||||
await repository.save(updates);
|
||||
}
|
||||
|
||||
const relatedMetadataNames = getMetadataRelatedMetadataNames(metadataName);
|
||||
const relatedCacheKeysToInvalidate = relatedMetadataNames.map(
|
||||
getMetadataFlatEntityMapsKey,
|
||||
);
|
||||
|
||||
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName);
|
||||
|
||||
this.logger.log(
|
||||
`Invalidating caches: ${flatEntityMapsKey} ${relatedCacheKeysToInvalidate.join(' ')}`,
|
||||
);
|
||||
|
||||
if (!dryRun) {
|
||||
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
|
||||
flatEntityMapsKey,
|
||||
...relatedCacheKeysToInvalidate,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private async identifyViewSortEntities({
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
dryRun,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
dryRun: boolean;
|
||||
}): Promise<void> {
|
||||
const viewSortRepository =
|
||||
this.coreDataSource.getRepository(ViewSortEntity);
|
||||
|
||||
const viewSortsWithoutApplicationId = await viewSortRepository.find({
|
||||
select: ['id', 'universalIdentifier', 'applicationId'],
|
||||
where: {
|
||||
workspaceId,
|
||||
applicationId: IsNull(),
|
||||
},
|
||||
});
|
||||
|
||||
if (viewSortsWithoutApplicationId.length === 0) {
|
||||
this.logger.log(
|
||||
`No viewSort entities found without applicationId for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const updates = viewSortsWithoutApplicationId.map((viewSort) => ({
|
||||
id: viewSort.id,
|
||||
universalIdentifier: viewSort.universalIdentifier ?? v4(),
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
}));
|
||||
|
||||
this.logger.log(
|
||||
`Found ${updates.length} viewSort entities to update for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
if (!dryRun) {
|
||||
await viewSortRepository.save(updates);
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import { makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768916632478-makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullable.util';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-16:make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration',
|
||||
description:
|
||||
'Make universalIdentifier and applicationId columns NOT NULL on remaining entities (roleTarget, rowLevelPermissionPredicate, rowLevelPermissionPredicateGroup, viewFilterGroup, viewSort, cronTrigger, databaseEventTrigger, routeTrigger, serverlessFunction, skill, pageLayoutWidget, pageLayout, pageLayoutTab)',
|
||||
})
|
||||
export class MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
private hasRunOnce = false;
|
||||
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
if (this.hasRunOnce) {
|
||||
this.logger.warn(
|
||||
'Skipping has already been run once MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
await makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableQueries(
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
this.logger.log(
|
||||
'Successfully run MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand',
|
||||
);
|
||||
this.hasRunOnce = true;
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(
|
||||
`Rolling back MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`,
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -16,6 +16,8 @@ import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand
|
||||
import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-index-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { IdentifyRemainingEntitiesMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command';
|
||||
import { MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-role-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
@@ -86,6 +88,8 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
IdentifyRemainingEntitiesMetadataCommand,
|
||||
],
|
||||
exports: [
|
||||
UpdateTaskOnDeleteActionCommand,
|
||||
@@ -109,6 +113,8 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
IdentifyRemainingEntitiesMetadataCommand,
|
||||
],
|
||||
})
|
||||
export class V1_16_UpgradeVersionCommandModule {}
|
||||
|
||||
+7
@@ -31,12 +31,14 @@ import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-ver
|
||||
import { IdentifyRoleMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-role-metadata.command';
|
||||
import { IdentifyViewFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command';
|
||||
import { IdentifyViewFilterMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command';
|
||||
import { IdentifyRemainingEntitiesMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command';
|
||||
import { IdentifyViewGroupMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-group-metadata.command';
|
||||
import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command';
|
||||
import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-index-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-role-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
import { MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command';
|
||||
@@ -103,6 +105,8 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly makeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
protected readonly makeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
protected readonly makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
protected readonly identifyRemainingEntitiesMetadataCommand: IdentifyRemainingEntitiesMetadataCommand,
|
||||
protected readonly makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
) {
|
||||
super(
|
||||
workspaceRepository,
|
||||
@@ -167,6 +171,9 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
.makeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
this
|
||||
.makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
this.identifyRemainingEntitiesMetadataCommand,
|
||||
this
|
||||
.makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
];
|
||||
|
||||
this.allCommands = {
|
||||
|
||||
Reference in New Issue
Block a user