Update standard fields (#6532)

In this PR:
- adding Favorites to Tasks and Notes
- fixing inconsistencies between custom object creation and sync of
standard fields of custom objects
- fixing workspaceCacheVersion not used to invalidate existing
datasource
This commit is contained in:
Charles Bochet
2024-08-04 23:22:41 +02:00
committed by GitHub
parent 03204021cb
commit 2b311b5f7b
14 changed files with 112 additions and 40 deletions

View File

@ -50,6 +50,7 @@ import {
CUSTOM_OBJECT_STANDARD_FIELD_IDS,
FAVORITE_STANDARD_FIELD_IDS,
NOTE_TARGET_STANDARD_FIELD_IDS,
TASK_TARGET_STANDARD_FIELD_IDS,
TIMELINE_ACTIVITY_STANDARD_FIELD_IDS,
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import {
@ -255,13 +256,13 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
standardId: BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt,
type: FieldMetadataType.DATE_TIME,
name: 'updatedAt',
label: 'Update date',
icon: 'IconCalendar',
description: 'Update date',
label: 'Last update',
icon: 'IconCalendarClock',
description: 'Last time the record was changed',
isNullable: false,
isActive: true,
isCustom: false,
isSystem: true,
isSystem: false,
workspaceId: objectMetadataInput.workspaceId,
defaultValue: 'now',
},
@ -269,7 +270,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.createdBy,
type: FieldMetadataType.ACTOR,
name: 'createdBy',
label: 'Created By',
label: 'Created by',
icon: 'IconCreativeCommonsSa',
description: 'The creator of the record',
isNullable: false,
@ -676,7 +677,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
name: 'noteTargets',
label: 'Notes',
description: `Notes tied to the ${createdObjectMetadata.labelSingular}`,
icon: 'IconCheckbox',
icon: 'IconNotes',
isNullable: true,
},
// TO
@ -746,7 +747,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
{
standardId: createForeignKeyDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
standardId: TASK_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: taskTargetObjectMetadata.id,
workspaceId: workspaceId,
@ -784,7 +785,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
{
standardId: createRelationDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
standardId: TASK_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: taskTargetObjectMetadata.id,
workspaceId: workspaceId,
@ -979,8 +980,9 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
name: 'timelineActivities',
label: 'Timeline Activities',
description: `Timeline Activities tied to the ${createdObjectMetadata.labelSingular}`,
icon: 'IconTimeline',
icon: 'IconIconTimelineEvent',
isNullable: true,
isSystem: true,
},
// TO
{
@ -996,7 +998,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular,
description: `Timeline Activity ${createdObjectMetadata.labelSingular}`,
icon: 'IconBuildingSkyscraper',
icon: 'IconTimeline',
isNullable: true,
},
]);
@ -1100,7 +1102,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular,
description: `Favorite ${createdObjectMetadata.labelSingular}`,
icon: 'IconBuildingSkyscraper',
icon: 'IconHeart',
isNullable: true,
},
]);

View File

@ -2,12 +2,12 @@ import { ObjectType } from 'typeorm';
import { WorkspaceDynamicRelationMetadataArgsFactory } from 'src/engine/twenty-orm/interfaces/workspace-dynamic-relation-metadata-args.interface';
import { TypedReflect } from 'src/utils/typed-reflect';
import {
RelationMetadataType,
RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
import { TypedReflect } from 'src/utils/typed-reflect';
interface WorkspaceBaseDynamicRelationOptions<TClass> {
type: RelationMetadataType;
@ -27,6 +27,7 @@ export function WorkspaceDynamicRelation<TClass extends object>(
target,
propertyKey.toString(),
) ?? false;
const gate = TypedReflect.getMetadata(
'workspace:gate-metadata-args',
target,

View File

@ -26,20 +26,20 @@ export class WorkspaceDatasourceFactory {
public async create(
workspaceId: string,
cacheVersion: string | null,
workspaceSchemaVersion: string | null,
): Promise<WorkspaceDataSource> {
const desiredCacheVersion =
cacheVersion ??
const desiredWorkspaceSchemaVersion =
workspaceSchemaVersion ??
(await this.workspaceCacheVersionService.getVersion(workspaceId));
if (!desiredCacheVersion) {
if (!desiredWorkspaceSchemaVersion) {
throw new Error('Cache version not found');
}
const latestCacheVersion =
const latestWorkspaceSchemaVersion =
await this.workspaceCacheVersionService.getVersion(workspaceId);
if (latestCacheVersion !== desiredCacheVersion) {
if (latestWorkspaceSchemaVersion !== desiredWorkspaceSchemaVersion) {
throw new Error('Cache version mismatch');
}
@ -70,7 +70,7 @@ export class WorkspaceDatasourceFactory {
}
const workspaceDataSource = await workspaceDataSourceCacheInstance.execute(
`${workspaceId}-${cacheVersion}`,
`${workspaceId}-${latestWorkspaceSchemaVersion}`,
async () => {
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId(

View File

@ -12,7 +12,6 @@ export class CacheManager<T> {
): Promise<T | null> {
const [workspaceId] = cacheKey.split('-');
// If the cacheKey exists, return the cached value
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!;
}

View File

@ -203,6 +203,8 @@ export const FAVORITE_STANDARD_FIELD_IDS = {
company: '20202020-cff5-4682-8bf9-069169e08279',
opportunity: '20202020-dabc-48e1-8318-2781a2b32aa2',
workflow: '20202020-b11b-4dc8-999a-6bd0a947b463',
task: '20202020-1b1b-4b3b-8b1b-7f8d6a1d7d5c',
note: '20202020-1f25-43fe-8b00-af212fdde824',
custom: '20202020-855a-4bc8-9861-79deef37011f',
};
@ -272,6 +274,7 @@ export const NOTE_STANDARD_FIELD_IDS = {
noteTargets: '20202020-1f25-43fe-8b00-af212fdde823',
attachments: '20202020-4986-4c92-bf19-39934b149b16',
timelineActivities: '20202020-7030-42f8-929c-1a57b25d6bce',
favorites: '20202020-4d1d-41ac-b13b-621631298d67',
};
export const NOTE_TARGET_STANDARD_FIELD_IDS = {
@ -334,6 +337,7 @@ export const TASK_STANDARD_FIELD_IDS = {
attachments: '20202020-794d-4783-a8ff-cecdb15be139',
assignee: '20202020-065a-4f42-a906-e20422c1753f',
timelineActivities: '20202020-c778-4278-99ee-23a2837aee64',
favorites: '20202020-4d1d-41ac-b13b-621631298d65',
};
export const TASK_TARGET_STANDARD_FIELD_IDS = {

View File

@ -1,22 +1,22 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { WorkspaceDynamicRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-dynamic-relation-metadata-args.interface';
import { WorkspaceEntityMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-entity-metadata-args.interface';
import { WorkspaceFieldMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface';
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
import {
PartialComputedFieldMetadata,
PartialFieldMetadata,
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
import { WorkspaceEntityMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-entity-metadata-args.interface';
import { WorkspaceFieldMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface';
import { WorkspaceDynamicRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-dynamic-relation-metadata-args.interface';
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { createDeterministicUuid } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
import { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
import { createDeterministicUuid } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
@Injectable()
export class StandardFieldFactory {
@ -163,9 +163,7 @@ export class StandardFieldFactory {
workspaceId: context.workspaceId,
isNullable: workspaceFieldMetadataArgs.isNullable,
isCustom: workspaceFieldMetadataArgs.isDeprecated ? true : false,
isSystem:
workspaceEntityMetadataArgs?.isSystem ||
workspaceFieldMetadataArgs.isSystem,
isSystem: workspaceFieldMetadataArgs.isSystem ?? false,
},
];
}

View File

@ -84,7 +84,7 @@ export class WorkspaceSyncMetadataService {
workspaceFeatureFlagsMap,
);
// 2 - Sync standard fields on custom objects
// 2 - Sync standard fields on standard and custom objects
const workspaceFieldMigrations =
await this.workspaceSyncFieldMetadataService.synchronize(
context,