mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-25 04:55:30 +03:00
Add new Address field to views containing deprecated address (#6205)
as per title, following introduction of new Address field, we want to display the new field next to the deprecated field, for users to notice the new field. <img width="983" alt="Capture d’écran 2024-07-10 à 17 44 25" src="https://github.com/twentyhq/twenty/assets/51697796/7b5309b4-b22d-4f32-8054-68bc7b0f3bb3">
This commit is contained in:
parent
70f46242b4
commit
8e25a107fd
@ -0,0 +1,230 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import {
|
||||
BillingSubscription,
|
||||
SubscriptionStatus,
|
||||
} from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import {
|
||||
FeatureFlagEntity,
|
||||
FeatureFlagKeys,
|
||||
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||
|
||||
interface AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommandOptions {
|
||||
workspaceId?: string;
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: 'migrate-0.22:add-new-address-field-to-views-with-deprecated-address-field',
|
||||
description: 'Adding new field Address to views containing old address field',
|
||||
})
|
||||
export class AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand extends CommandRunner {
|
||||
private readonly logger = new Logger(
|
||||
AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand.name,
|
||||
);
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(BillingSubscription, 'core')
|
||||
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
||||
@InjectRepository(FeatureFlagEntity, 'core')
|
||||
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
||||
private readonly twentyORMManager: TwentyORMManager,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-w, --workspace-id [workspace_id]',
|
||||
description: 'workspace id. Command runs on all workspaces if not provided',
|
||||
required: false,
|
||||
})
|
||||
parseWorkspaceId(value: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
async run(
|
||||
_passedParam: string[],
|
||||
options: AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommandOptions,
|
||||
): Promise<void> {
|
||||
// This command can be generic-ified turning the below consts in options
|
||||
const deprecatedFieldStandardId =
|
||||
COMPANY_STANDARD_FIELD_IDS.address_deprecated;
|
||||
const newFieldStandardId = COMPANY_STANDARD_FIELD_IDS.address;
|
||||
|
||||
this.logger.log('running');
|
||||
let workspaceIds: string[] = [];
|
||||
|
||||
if (options.workspaceId) {
|
||||
workspaceIds = [options.workspaceId];
|
||||
} else {
|
||||
const workspaces = await this.workspaceRepository.find();
|
||||
|
||||
const activeWorkspaceIds = (
|
||||
await Promise.all(
|
||||
workspaces.map(async (workspace) => {
|
||||
const isActive = await this.workspaceIsActive(workspace);
|
||||
|
||||
return { workspace, isActive };
|
||||
}),
|
||||
)
|
||||
)
|
||||
.filter((result) => result.isActive)
|
||||
.map((result) => result.workspace.id);
|
||||
|
||||
workspaceIds = activeWorkspaceIds;
|
||||
}
|
||||
|
||||
if (!workspaceIds.length) {
|
||||
this.logger.log(chalk.yellow('No workspace found'));
|
||||
|
||||
return;
|
||||
} else {
|
||||
this.logger.log(
|
||||
chalk.green(`Running command on ${workspaceIds.length} workspaces`),
|
||||
);
|
||||
}
|
||||
|
||||
for (const workspaceId of workspaceIds) {
|
||||
const viewFieldRepository =
|
||||
await this.twentyORMManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
ViewFieldWorkspaceEntity,
|
||||
);
|
||||
|
||||
const dataSourceMetadatas =
|
||||
await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
for (const dataSourceMetadata of dataSourceMetadatas) {
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
if (workspaceDataSource) {
|
||||
try {
|
||||
const newAddressField = await this.fieldMetadataRepository.findBy({
|
||||
workspaceId,
|
||||
standardId: newFieldStandardId,
|
||||
});
|
||||
|
||||
if (isEmpty(newAddressField)) {
|
||||
this.logger.log(
|
||||
`Error - missing new Address standard field of type Address, please run workspace-sync-metadata on your workspace (${workspaceId}) before running this command`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const addressDeprecatedField =
|
||||
await this.fieldMetadataRepository.findOneBy({
|
||||
workspaceId,
|
||||
standardId: deprecatedFieldStandardId,
|
||||
});
|
||||
|
||||
if (isEmpty(addressDeprecatedField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const viewsWithAddressDeprecatedField =
|
||||
await viewFieldRepository.find({
|
||||
where: {
|
||||
fieldMetadataId: addressDeprecatedField.id,
|
||||
isVisible: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const viewWithAddressDeprecatedField of viewsWithAddressDeprecatedField) {
|
||||
const viewId = viewWithAddressDeprecatedField.viewId;
|
||||
|
||||
const newAddressFieldInThisView =
|
||||
await viewFieldRepository.findBy({
|
||||
fieldMetadataId: newAddressField[0].id,
|
||||
viewId: viewWithAddressDeprecatedField.viewId as string,
|
||||
isVisible: true,
|
||||
});
|
||||
|
||||
if (!isEmpty(newAddressFieldInThisView)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Adding new address field to view ${viewId} for workspace ${workspaceId}...`,
|
||||
);
|
||||
const newViewField = viewFieldRepository.create({
|
||||
viewId: viewWithAddressDeprecatedField.viewId,
|
||||
fieldMetadataId: newAddressField[0].id,
|
||||
position: viewWithAddressDeprecatedField.position - 0.5,
|
||||
isVisible: true,
|
||||
});
|
||||
|
||||
await viewFieldRepository.save(newViewField);
|
||||
this.logger.log(
|
||||
`New address field successfully added to view ${viewId} for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.log(
|
||||
chalk.red(`Running command on workspace ${workspaceId} failed`),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.workspaceCacheVersionService.incrementVersion(workspaceId);
|
||||
|
||||
this.logger.log(
|
||||
chalk.green(`Running command on workspace ${workspaceId} done`),
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(chalk.green(`Command completed!`));
|
||||
}
|
||||
|
||||
private async workspaceIsActive(workspace: Workspace): Promise<boolean> {
|
||||
const billingSupscriptionForWorkspace =
|
||||
await this.billingSubscriptionRepository.findOne({
|
||||
where: { workspaceId: workspace.id },
|
||||
});
|
||||
|
||||
if (
|
||||
billingSupscriptionForWorkspace?.status &&
|
||||
[
|
||||
SubscriptionStatus.PastDue,
|
||||
SubscriptionStatus.Active,
|
||||
SubscriptionStatus.Trialing,
|
||||
].includes(billingSupscriptionForWorkspace.status as SubscriptionStatus)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const freeAccessEnabledFeatureFlagForWorkspace =
|
||||
await this.featureFlagRepository.findOne({
|
||||
where: {
|
||||
workspaceId: workspace.id,
|
||||
key: FeatureFlagKeys.IsFreeAccessEnabled,
|
||||
value: true,
|
||||
},
|
||||
});
|
||||
|
||||
return !!freeAccessEnabledFeatureFlagForWorkspace;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { UpdateMessageChannelSyncStatusEnumCommand } from 'src/database/commands/0-20-update-message-channel-sync-status-enum.command';
|
||||
import { AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand } from 'src/database/commands/0-22-add-new-address-field-to-views-with-deprecated-address.command';
|
||||
import { StartDataSeedDemoWorkspaceCronCommand } from 'src/database/commands/data-seed-demo-workspace/crons/start-data-seed-demo-workspace.cron.command';
|
||||
import { StopDataSeedDemoWorkspaceCronCommand } from 'src/database/commands/data-seed-demo-workspace/crons/stop-data-seed-demo-workspace.cron.command';
|
||||
import { DataSeedDemoWorkspaceCommand } from 'src/database/commands/data-seed-demo-workspace/data-seed-demo-workspace-command';
|
||||
@ -12,6 +13,8 @@ import { UpdateMessageChannelVisibilityEnumCommand } from 'src/database/commands
|
||||
import { UpgradeTo0_22CommandModule } from 'src/database/commands/upgrade-version/0-22/0-22-upgrade-version.module';
|
||||
import { WorkspaceAddTotalCountCommand } from 'src/database/commands/workspace-add-total-count.command';
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
@ -28,7 +31,10 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
||||
WorkspaceManagerModule,
|
||||
DataSourceModule,
|
||||
TypeORMModule,
|
||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||
TypeOrmModule.forFeature(
|
||||
[Workspace, BillingSubscription, FeatureFlagEntity],
|
||||
'core',
|
||||
),
|
||||
TypeOrmModule.forFeature(
|
||||
[FieldMetadataEntity, ObjectMetadataEntity],
|
||||
'metadata',
|
||||
@ -52,6 +58,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
||||
StopDataSeedDemoWorkspaceCronCommand,
|
||||
UpdateMessageChannelVisibilityEnumCommand,
|
||||
UpdateMessageChannelSyncStatusEnumCommand,
|
||||
AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand,
|
||||
],
|
||||
})
|
||||
export class DatabaseCommandModule {}
|
||||
|
@ -41,7 +41,7 @@ export class EntityEventsToDbListener {
|
||||
// ....
|
||||
|
||||
private async handle(payload: ObjectRecordBaseEvent) {
|
||||
if (!payload.objectMetadata.isAuditLogged) {
|
||||
if (!payload.objectMetadata?.isAuditLogged) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,14 @@ import {
|
||||
RelationMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspace-is-deprecated.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityTargetWorkspaceEntity } from 'src/modules/activity/standard-objects/activity-target.workspace-entity';
|
||||
@ -16,16 +24,8 @@ import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objec
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspace-is-deprecated.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.company,
|
||||
@ -101,7 +101,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Address (deprecated) ',
|
||||
description:
|
||||
'Address of the company - deprecated in favor of new address field',
|
||||
"This standard field has been deprecated and migrated as a custom field. Please consider using the new 'address' field type.",
|
||||
icon: 'IconMap',
|
||||
})
|
||||
@WorkspaceIsDeprecated()
|
||||
|
Loading…
Reference in New Issue
Block a user