Synchronization between Core Views and Workspace Views (#13461)
Closes https://github.com/twentyhq/core-team-issues/issues/1248 - Create listeners on each CRUD operation for all view related objects and update the core views accordingly Some fields have to be parsed since we changed the data model a little bit when switching to core views.
This commit is contained in:
@ -26,6 +26,7 @@ import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/vie
|
|||||||
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||||
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
import { transformViewFilterWorkspaceValueToCoreValue } from 'src/modules/view/utils/transform-view-filter-workspace-value-to-core-value';
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'migrate:views-to-core',
|
name: 'migrate:views-to-core',
|
||||||
@ -298,22 +299,12 @@ export class MigrateViewsToCoreCommand extends ActiveOrSuspendedWorkspacesMigrat
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsedValue: JSON;
|
|
||||||
|
|
||||||
try {
|
|
||||||
parsedValue = JSON.parse(filter.value);
|
|
||||||
} catch {
|
|
||||||
throw new Error(
|
|
||||||
`Could not parse value to JSON for view filter ${filter.id} for workspace ${workspaceId}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const coreViewFilter: Partial<ViewFilter> = {
|
const coreViewFilter: Partial<ViewFilter> = {
|
||||||
id: filter.id,
|
id: filter.id,
|
||||||
fieldMetadataId: filter.fieldMetadataId,
|
fieldMetadataId: filter.fieldMetadataId,
|
||||||
viewId: filter.viewId,
|
viewId: filter.viewId,
|
||||||
operand: filter.operand,
|
operand: filter.operand,
|
||||||
value: parsedValue,
|
value: transformViewFilterWorkspaceValueToCoreValue(filter.value),
|
||||||
viewFilterGroupId: filter.viewFilterGroupId,
|
viewFilterGroupId: filter.viewFilterGroupId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
createdAt: new Date(filter.createdAt),
|
createdAt: new Date(filter.createdAt),
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
export type RelationFilterValue = {
|
||||||
|
isCurrentWorkspaceMemberSelected?: boolean;
|
||||||
|
selectedRecordIds: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ViewFilterValue =
|
||||||
|
| string
|
||||||
|
| string[]
|
||||||
|
| RelationFilterValue
|
||||||
|
| Record<string, unknown>
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
@ -14,6 +14,7 @@ import {
|
|||||||
|
|
||||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { ViewFilterValue } from 'src/engine/metadata-modules/view/types/view-filter-value.type';
|
||||||
import { View } from 'src/engine/metadata-modules/view/view.entity';
|
import { View } from 'src/engine/metadata-modules/view/view.entity';
|
||||||
|
|
||||||
@Entity({ name: 'viewFilter', schema: 'core' })
|
@Entity({ name: 'viewFilter', schema: 'core' })
|
||||||
@ -31,7 +32,7 @@ export class ViewFilter {
|
|||||||
operand: string;
|
operand: string;
|
||||||
|
|
||||||
@Column({ nullable: false, type: 'jsonb' })
|
@Column({ nullable: false, type: 'jsonb' })
|
||||||
value: JSON;
|
value: ViewFilterValue;
|
||||||
|
|
||||||
@Column({ nullable: true, type: 'uuid' })
|
@Column({ nullable: true, type: 'uuid' })
|
||||||
viewFilterGroupId?: string | null;
|
viewFilterGroupId?: string | null;
|
||||||
|
|||||||
@ -0,0 +1,209 @@
|
|||||||
|
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||||
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import {
|
||||||
|
ViewException,
|
||||||
|
ViewExceptionCode,
|
||||||
|
} from 'src/modules/view/views.exception';
|
||||||
|
|
||||||
|
type EntityWithId = { id: string };
|
||||||
|
|
||||||
|
type SyncOperations<T extends EntityWithId> = {
|
||||||
|
create: (workspaceId: string, entity: T) => Promise<void>;
|
||||||
|
update: (
|
||||||
|
workspaceId: string,
|
||||||
|
entity: T,
|
||||||
|
diff?: Partial<ObjectRecordDiff<T>>,
|
||||||
|
) => Promise<void>;
|
||||||
|
delete: (workspaceId: string, entity: Pick<T, 'id'>) => Promise<void>;
|
||||||
|
destroy: (workspaceId: string, entity: Pick<T, 'id'>) => Promise<void>;
|
||||||
|
restore: (workspaceId: string, entity: Pick<T, 'id'>) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export abstract class BaseViewSyncListener<T extends EntityWithId> {
|
||||||
|
@Inject(FeatureFlagService)
|
||||||
|
protected readonly featureFlagService: FeatureFlagService;
|
||||||
|
|
||||||
|
@Inject(ExceptionHandlerService)
|
||||||
|
protected readonly exceptionHandlerService: ExceptionHandlerService;
|
||||||
|
|
||||||
|
protected readonly logger: Logger;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly syncOperations: SyncOperations<T>,
|
||||||
|
loggerName: string,
|
||||||
|
protected readonly entityTypeName: string,
|
||||||
|
) {
|
||||||
|
this.logger = new Logger(loggerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<ObjectRecordCreateEvent<T>>,
|
||||||
|
): Promise<void> {
|
||||||
|
const isEnabled = await this.isFeatureFlagEnabled(batchEvent.workspaceId);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of batchEvent.events) {
|
||||||
|
try {
|
||||||
|
await this.syncOperations.create(
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
event.properties.after,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.captureException(
|
||||||
|
error,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
'create',
|
||||||
|
event.properties.after.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<ObjectRecordUpdateEvent<T>>,
|
||||||
|
): Promise<void> {
|
||||||
|
const isEnabled = await this.isFeatureFlagEnabled(batchEvent.workspaceId);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of batchEvent.events) {
|
||||||
|
try {
|
||||||
|
await this.syncOperations.update(
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
event.properties.after,
|
||||||
|
event.properties.diff,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.captureException(
|
||||||
|
error,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
'update',
|
||||||
|
event.properties.after.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<ObjectRecordDeleteEvent<T>>,
|
||||||
|
): Promise<void> {
|
||||||
|
const isEnabled = await this.isFeatureFlagEnabled(batchEvent.workspaceId);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of batchEvent.events) {
|
||||||
|
try {
|
||||||
|
await this.syncOperations.delete(
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
event.properties.before,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.captureException(
|
||||||
|
error,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
'delete',
|
||||||
|
event.properties.before.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<ObjectRecordDestroyEvent<T>>,
|
||||||
|
): Promise<void> {
|
||||||
|
const isEnabled = await this.isFeatureFlagEnabled(batchEvent.workspaceId);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of batchEvent.events) {
|
||||||
|
try {
|
||||||
|
await this.syncOperations.destroy(
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
event.properties.before,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.captureException(
|
||||||
|
error,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
'destroy',
|
||||||
|
event.properties.before.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<ObjectRecordRestoreEvent<T>>,
|
||||||
|
): Promise<void> {
|
||||||
|
const isEnabled = await this.isFeatureFlagEnabled(batchEvent.workspaceId);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of batchEvent.events) {
|
||||||
|
try {
|
||||||
|
await this.syncOperations.restore(
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
event.properties.after,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.captureException(
|
||||||
|
error,
|
||||||
|
batchEvent.workspaceId,
|
||||||
|
'restore',
|
||||||
|
event.properties.after.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async isFeatureFlagEnabled(workspaceId: string): Promise<boolean> {
|
||||||
|
const featureFlags =
|
||||||
|
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
|
||||||
|
|
||||||
|
return featureFlags.IS_CORE_VIEW_SYNCING_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private captureException(
|
||||||
|
error: Error,
|
||||||
|
workspaceId: string,
|
||||||
|
operation: string,
|
||||||
|
entityId: string,
|
||||||
|
) {
|
||||||
|
const viewException = new ViewException(
|
||||||
|
`Failed to sync ${this.entityTypeName} ${entityId} to core: ${error.message}`,
|
||||||
|
ViewExceptionCode.CORE_VIEW_SYNC_ERROR,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.exceptionHandlerService.captureExceptions([viewException], {
|
||||||
|
workspace: {
|
||||||
|
id: workspaceId,
|
||||||
|
},
|
||||||
|
additionalData: {
|
||||||
|
entityId: entityId,
|
||||||
|
entityType: this.entityTypeName,
|
||||||
|
operation: operation,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewFieldSyncService } from 'src/modules/view/services/view-field-sync.service';
|
||||||
|
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFieldListener extends BaseViewSyncListener<ViewFieldWorkspaceEntity> {
|
||||||
|
constructor(viewFieldSyncService: ViewFieldSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create:
|
||||||
|
viewFieldSyncService.createCoreViewField.bind(viewFieldSyncService),
|
||||||
|
update:
|
||||||
|
viewFieldSyncService.updateCoreViewField.bind(viewFieldSyncService),
|
||||||
|
delete:
|
||||||
|
viewFieldSyncService.deleteCoreViewField.bind(viewFieldSyncService),
|
||||||
|
destroy:
|
||||||
|
viewFieldSyncService.destroyCoreViewField.bind(viewFieldSyncService),
|
||||||
|
restore:
|
||||||
|
viewFieldSyncService.restoreCoreViewField.bind(viewFieldSyncService),
|
||||||
|
},
|
||||||
|
ViewFieldListener.name,
|
||||||
|
'view field',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewField', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewFieldCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewFieldWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewField', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewFieldUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewFieldWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewField', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewFieldDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewFieldWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewField', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewFieldDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewFieldWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewField', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewFieldRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewFieldWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewFilterGroupSyncService } from 'src/modules/view/services/view-filter-group-sync.service';
|
||||||
|
import { ViewFilterGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter-group.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFilterGroupListener extends BaseViewSyncListener<ViewFilterGroupWorkspaceEntity> {
|
||||||
|
constructor(viewFilterGroupSyncService: ViewFilterGroupSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create: viewFilterGroupSyncService.createCoreViewFilterGroup.bind(
|
||||||
|
viewFilterGroupSyncService,
|
||||||
|
),
|
||||||
|
update: viewFilterGroupSyncService.updateCoreViewFilterGroup.bind(
|
||||||
|
viewFilterGroupSyncService,
|
||||||
|
),
|
||||||
|
delete: viewFilterGroupSyncService.deleteCoreViewFilterGroup.bind(
|
||||||
|
viewFilterGroupSyncService,
|
||||||
|
),
|
||||||
|
destroy: viewFilterGroupSyncService.destroyCoreViewFilterGroup.bind(
|
||||||
|
viewFilterGroupSyncService,
|
||||||
|
),
|
||||||
|
restore: viewFilterGroupSyncService.restoreCoreViewFilterGroup.bind(
|
||||||
|
viewFilterGroupSyncService,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ViewFilterGroupListener.name,
|
||||||
|
'view filter group',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilterGroup', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewFilterGroupCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewFilterGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilterGroup', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewFilterGroupUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewFilterGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilterGroup', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewFilterGroupDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewFilterGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilterGroup', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewFilterGroupDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewFilterGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilterGroup', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewFilterGroupRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewFilterGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewFilterSyncService } from 'src/modules/view/services/view-filter-sync.service';
|
||||||
|
import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFilterListener extends BaseViewSyncListener<ViewFilterWorkspaceEntity> {
|
||||||
|
constructor(viewFilterSyncService: ViewFilterSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create: viewFilterSyncService.createCoreViewFilter.bind(
|
||||||
|
viewFilterSyncService,
|
||||||
|
),
|
||||||
|
update: viewFilterSyncService.updateCoreViewFilter.bind(
|
||||||
|
viewFilterSyncService,
|
||||||
|
),
|
||||||
|
delete: viewFilterSyncService.deleteCoreViewFilter.bind(
|
||||||
|
viewFilterSyncService,
|
||||||
|
),
|
||||||
|
destroy: viewFilterSyncService.destroyCoreViewFilter.bind(
|
||||||
|
viewFilterSyncService,
|
||||||
|
),
|
||||||
|
restore: viewFilterSyncService.restoreCoreViewFilter.bind(
|
||||||
|
viewFilterSyncService,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ViewFilterListener.name,
|
||||||
|
'view filter',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilter', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewFilterCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewFilterWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilter', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewFilterUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewFilterWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilter', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewFilterDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewFilterWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilter', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewFilterDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewFilterWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewFilter', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewFilterRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewFilterWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewGroupSyncService } from 'src/modules/view/services/view-group-sync.service';
|
||||||
|
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewGroupListener extends BaseViewSyncListener<ViewGroupWorkspaceEntity> {
|
||||||
|
constructor(viewGroupSyncService: ViewGroupSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create:
|
||||||
|
viewGroupSyncService.createCoreViewGroup.bind(viewGroupSyncService),
|
||||||
|
update:
|
||||||
|
viewGroupSyncService.updateCoreViewGroup.bind(viewGroupSyncService),
|
||||||
|
delete:
|
||||||
|
viewGroupSyncService.deleteCoreViewGroup.bind(viewGroupSyncService),
|
||||||
|
destroy:
|
||||||
|
viewGroupSyncService.destroyCoreViewGroup.bind(viewGroupSyncService),
|
||||||
|
restore:
|
||||||
|
viewGroupSyncService.restoreCoreViewGroup.bind(viewGroupSyncService),
|
||||||
|
},
|
||||||
|
ViewGroupListener.name,
|
||||||
|
'view group',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewGroup', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewGroupCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewGroup', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewGroupUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewGroup', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewGroupDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewGroup', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewGroupDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewGroup', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewGroupRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewGroupWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewSortSyncService } from 'src/modules/view/services/view-sort-sync.service';
|
||||||
|
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewSortListener extends BaseViewSyncListener<ViewSortWorkspaceEntity> {
|
||||||
|
constructor(viewSortSyncService: ViewSortSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create:
|
||||||
|
viewSortSyncService.createCoreViewSort.bind(viewSortSyncService),
|
||||||
|
update:
|
||||||
|
viewSortSyncService.updateCoreViewSort.bind(viewSortSyncService),
|
||||||
|
delete:
|
||||||
|
viewSortSyncService.deleteCoreViewSort.bind(viewSortSyncService),
|
||||||
|
destroy:
|
||||||
|
viewSortSyncService.destroyCoreViewSort.bind(viewSortSyncService),
|
||||||
|
restore:
|
||||||
|
viewSortSyncService.restoreCoreViewSort.bind(viewSortSyncService),
|
||||||
|
},
|
||||||
|
ViewSortListener.name,
|
||||||
|
'view sort',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewSort', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewSortCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewSortWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewSort', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewSortUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewSortWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewSort', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewSortDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewSortWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewSort', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewSortDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewSortWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('viewSort', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewSortRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewSortWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
|
||||||
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
|
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||||
|
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||||
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
|
import { ObjectRecordRestoreEvent } from 'src/engine/core-modules/event-emitter/types/object-record-restore.event';
|
||||||
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
|
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||||
|
import { ViewSyncService } from 'src/modules/view/services/view-sync.service';
|
||||||
|
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
|
||||||
|
import { BaseViewSyncListener } from './base-view-sync.listener';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewListener extends BaseViewSyncListener<ViewWorkspaceEntity> {
|
||||||
|
constructor(viewSyncService: ViewSyncService) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
create: viewSyncService.createCoreView.bind(viewSyncService),
|
||||||
|
update: viewSyncService.updateCoreView.bind(viewSyncService),
|
||||||
|
delete: viewSyncService.deleteCoreView.bind(viewSyncService),
|
||||||
|
destroy: viewSyncService.destroyCoreView.bind(viewSyncService),
|
||||||
|
restore: viewSyncService.restoreCoreView.bind(viewSyncService),
|
||||||
|
},
|
||||||
|
ViewListener.name,
|
||||||
|
'view',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('view', DatabaseEventAction.CREATED)
|
||||||
|
async handleViewCreated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordCreateEvent<ViewWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleCreated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('view', DatabaseEventAction.UPDATED)
|
||||||
|
async handleViewUpdated(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordUpdateEvent<ViewWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleUpdated(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('view', DatabaseEventAction.DELETED)
|
||||||
|
async handleViewDeleted(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDeleteEvent<ViewWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDeleted(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('view', DatabaseEventAction.DESTROYED)
|
||||||
|
async handleViewDestroyed(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordDestroyEvent<ViewWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleDestroyed(batchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnDatabaseBatchEvent('view', DatabaseEventAction.RESTORED)
|
||||||
|
async handleViewRestored(
|
||||||
|
batchEvent: WorkspaceEventBatch<
|
||||||
|
ObjectRecordRestoreEvent<ViewWorkspaceEntity>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
return this.handleRestored(batchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewField } from 'src/engine/metadata-modules/view/view-field.entity';
|
||||||
|
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFieldSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ViewField, 'core')
|
||||||
|
private readonly coreViewFieldRepository: Repository<ViewField>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewFieldWorkspaceEntity>>,
|
||||||
|
): Partial<ViewField> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewFieldWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<ViewField>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreViewField(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewField: ViewFieldWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
const coreViewField: Partial<ViewField> = {
|
||||||
|
id: workspaceViewField.id,
|
||||||
|
fieldMetadataId: workspaceViewField.fieldMetadataId,
|
||||||
|
viewId: workspaceViewField.viewId,
|
||||||
|
position: workspaceViewField.position,
|
||||||
|
isVisible: workspaceViewField.isVisible,
|
||||||
|
size: workspaceViewField.size,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceViewField.createdAt),
|
||||||
|
updatedAt: new Date(workspaceViewField.updatedAt),
|
||||||
|
deletedAt: workspaceViewField.deletedAt
|
||||||
|
? new Date(workspaceViewField.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewFieldRepository.save(coreViewField);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreViewField(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewField: Pick<ViewFieldWorkspaceEntity, 'id'>,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewFieldWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewFieldRepository.update(
|
||||||
|
{ id: workspaceViewField.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreViewField(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewField: Pick<ViewFieldWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFieldRepository.softDelete({
|
||||||
|
id: workspaceViewField.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreViewField(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewField: Pick<ViewFieldWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFieldRepository.delete({
|
||||||
|
id: workspaceViewField.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreViewField(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewField: Pick<ViewFieldWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFieldRepository.restore({
|
||||||
|
id: workspaceViewField.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view/enums/view-filter-group-logical-operator';
|
||||||
|
import { ViewFilterGroup } from 'src/engine/metadata-modules/view/view-filter-group.entity';
|
||||||
|
import { ViewFilterGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter-group.workspace-entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFilterGroupSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ViewFilterGroup, 'core')
|
||||||
|
private readonly coreViewFilterGroupRepository: Repository<ViewFilterGroup>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewFilterGroupWorkspaceEntity>>,
|
||||||
|
): Partial<ViewFilterGroup> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewFilterGroupWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
if (key === 'logicalOperator') {
|
||||||
|
updateData[key] = diffValue.after as ViewFilterGroupLogicalOperator;
|
||||||
|
} else {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<ViewFilterGroup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreViewFilterGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilterGroup: ViewFilterGroupWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
const coreViewFilterGroup: Partial<ViewFilterGroup> = {
|
||||||
|
id: workspaceViewFilterGroup.id,
|
||||||
|
viewId: workspaceViewFilterGroup.viewId,
|
||||||
|
logicalOperator:
|
||||||
|
workspaceViewFilterGroup.logicalOperator as ViewFilterGroupLogicalOperator,
|
||||||
|
parentViewFilterGroupId: workspaceViewFilterGroup.parentViewFilterGroupId,
|
||||||
|
positionInViewFilterGroup:
|
||||||
|
workspaceViewFilterGroup.positionInViewFilterGroup,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceViewFilterGroup.createdAt),
|
||||||
|
updatedAt: new Date(workspaceViewFilterGroup.updatedAt),
|
||||||
|
deletedAt: workspaceViewFilterGroup.deletedAt
|
||||||
|
? new Date(workspaceViewFilterGroup.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewFilterGroupRepository.save(coreViewFilterGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreViewFilterGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilterGroup: Pick<ViewFilterGroupWorkspaceEntity, 'id'>,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewFilterGroupWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewFilterGroupRepository.update(
|
||||||
|
{ id: workspaceViewFilterGroup.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreViewFilterGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilterGroup: Pick<ViewFilterGroupWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterGroupRepository.softDelete({
|
||||||
|
id: workspaceViewFilterGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreViewFilterGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilterGroup: Pick<ViewFilterGroupWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterGroupRepository.delete({
|
||||||
|
id: workspaceViewFilterGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreViewFilterGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilterGroup: ViewFilterGroupWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterGroupRepository.restore({
|
||||||
|
id: workspaceViewFilterGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewFilter } from 'src/engine/metadata-modules/view/view-filter.entity';
|
||||||
|
import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter.workspace-entity';
|
||||||
|
import { transformViewFilterWorkspaceValueToCoreValue } from 'src/modules/view/utils/transform-view-filter-workspace-value-to-core-value';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewFilterSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ViewFilter, 'core')
|
||||||
|
private readonly coreViewFilterRepository: Repository<ViewFilter>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewFilterWorkspaceEntity>>,
|
||||||
|
): Partial<ViewFilter> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewFilterWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
if (key === 'value' && typeof diffValue.after === 'string') {
|
||||||
|
updateData[key] = transformViewFilterWorkspaceValueToCoreValue(
|
||||||
|
diffValue.after,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<ViewFilter>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreViewFilter(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilter: ViewFilterWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewFilter.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const coreViewFilter: Partial<ViewFilter> = {
|
||||||
|
id: workspaceViewFilter.id,
|
||||||
|
fieldMetadataId: workspaceViewFilter.fieldMetadataId,
|
||||||
|
viewId: workspaceViewFilter.viewId,
|
||||||
|
operand: workspaceViewFilter.operand,
|
||||||
|
value: transformViewFilterWorkspaceValueToCoreValue(
|
||||||
|
workspaceViewFilter.value,
|
||||||
|
),
|
||||||
|
viewFilterGroupId: workspaceViewFilter.viewFilterGroupId,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceViewFilter.createdAt),
|
||||||
|
updatedAt: new Date(workspaceViewFilter.updatedAt),
|
||||||
|
deletedAt: workspaceViewFilter.deletedAt
|
||||||
|
? new Date(workspaceViewFilter.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewFilterRepository.save(coreViewFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreViewFilter(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilter: ViewFilterWorkspaceEntity,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewFilterWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewFilter.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewFilterRepository.update(
|
||||||
|
{ id: workspaceViewFilter.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreViewFilter(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilter: Pick<ViewFilterWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterRepository.softDelete({
|
||||||
|
id: workspaceViewFilter.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreViewFilter(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilter: Pick<ViewFilterWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterRepository.delete({
|
||||||
|
id: workspaceViewFilter.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreViewFilter(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewFilter: Pick<ViewFilterWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewFilterRepository.restore({
|
||||||
|
id: workspaceViewFilter.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewGroup } from 'src/engine/metadata-modules/view/view-group.entity';
|
||||||
|
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewGroupSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ViewGroup, 'core')
|
||||||
|
private readonly coreViewGroupRepository: Repository<ViewGroup>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewGroupWorkspaceEntity>>,
|
||||||
|
): Partial<ViewGroup> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewGroupWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<ViewGroup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreViewGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewGroup: ViewGroupWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewGroup.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const coreViewGroup: Partial<ViewGroup> = {
|
||||||
|
id: workspaceViewGroup.id,
|
||||||
|
fieldMetadataId: workspaceViewGroup.fieldMetadataId,
|
||||||
|
viewId: workspaceViewGroup.viewId,
|
||||||
|
fieldValue: workspaceViewGroup.fieldValue,
|
||||||
|
isVisible: workspaceViewGroup.isVisible,
|
||||||
|
position: workspaceViewGroup.position,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceViewGroup.createdAt),
|
||||||
|
updatedAt: new Date(workspaceViewGroup.updatedAt),
|
||||||
|
deletedAt: workspaceViewGroup.deletedAt
|
||||||
|
? new Date(workspaceViewGroup.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewGroupRepository.save(coreViewGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreViewGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewGroup: ViewGroupWorkspaceEntity,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewGroupWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewGroup.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewGroupRepository.update(
|
||||||
|
{ id: workspaceViewGroup.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreViewGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewGroup: Pick<ViewGroupWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewGroupRepository.softDelete({
|
||||||
|
id: workspaceViewGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreViewGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewGroup: Pick<ViewGroupWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewGroupRepository.delete({
|
||||||
|
id: workspaceViewGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreViewGroup(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewGroup: Pick<ViewGroupWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewGroupRepository.restore({
|
||||||
|
id: workspaceViewGroup.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewSortDirection } from 'src/engine/metadata-modules/view/enums/view-sort-direction';
|
||||||
|
import { ViewSort } from 'src/engine/metadata-modules/view/view-sort.entity';
|
||||||
|
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewSortSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ViewSort, 'core')
|
||||||
|
private readonly coreViewSortRepository: Repository<ViewSort>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewSortWorkspaceEntity>>,
|
||||||
|
): Partial<ViewSort> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewSortWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
if (key === 'direction') {
|
||||||
|
updateData[key] = (
|
||||||
|
diffValue.after as string
|
||||||
|
).toUpperCase() as ViewSortDirection;
|
||||||
|
} else {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<ViewSort>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreViewSort(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewSort: ViewSortWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewSort.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const direction =
|
||||||
|
workspaceViewSort.direction.toUpperCase() as ViewSortDirection;
|
||||||
|
|
||||||
|
const coreViewSort: Partial<ViewSort> = {
|
||||||
|
id: workspaceViewSort.id,
|
||||||
|
fieldMetadataId: workspaceViewSort.fieldMetadataId,
|
||||||
|
viewId: workspaceViewSort.viewId,
|
||||||
|
direction: direction,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceViewSort.createdAt),
|
||||||
|
updatedAt: new Date(workspaceViewSort.updatedAt),
|
||||||
|
deletedAt: workspaceViewSort.deletedAt
|
||||||
|
? new Date(workspaceViewSort.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewSortRepository.save(coreViewSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreViewSort(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewSort: ViewSortWorkspaceEntity,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewSortWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!workspaceViewSort.viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewSortRepository.update(
|
||||||
|
{ id: workspaceViewSort.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreViewSort(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewSort: Pick<ViewSortWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewSortRepository.softDelete({
|
||||||
|
id: workspaceViewSort.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreViewSort(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewSort: Pick<ViewSortWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewSortRepository.delete({
|
||||||
|
id: workspaceViewSort.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreViewSort(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceViewSort: Pick<ViewSortWorkspaceEntity, 'id'>,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewSortRepository.restore({
|
||||||
|
id: workspaceViewSort.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ObjectRecordDiff } from 'src/engine/core-modules/event-emitter/types/object-record-diff';
|
||||||
|
import { ViewOpenRecordIn } from 'src/engine/metadata-modules/view/enums/view-open-record-in';
|
||||||
|
import { View } from 'src/engine/metadata-modules/view/view.entity';
|
||||||
|
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewSyncService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(View, 'core')
|
||||||
|
private readonly coreViewRepository: Repository<View>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private parseUpdateDataFromDiff(
|
||||||
|
diff: Partial<ObjectRecordDiff<ViewWorkspaceEntity>>,
|
||||||
|
): Partial<View> {
|
||||||
|
const updateData: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(diff)) {
|
||||||
|
const diffValue = diff[key as keyof ViewWorkspaceEntity];
|
||||||
|
|
||||||
|
if (isDefined(diffValue)) {
|
||||||
|
if (key === 'openRecordIn') {
|
||||||
|
updateData[key] =
|
||||||
|
diffValue.after === 'SIDE_PANEL'
|
||||||
|
? ViewOpenRecordIn.SIDE_PANEL
|
||||||
|
: ViewOpenRecordIn.RECORD_PAGE;
|
||||||
|
} else {
|
||||||
|
updateData[key] = diffValue.after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateData as Partial<View>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCoreView(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceView: ViewWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
const coreView: Partial<View> = {
|
||||||
|
id: workspaceView.id,
|
||||||
|
name: workspaceView.name,
|
||||||
|
objectMetadataId: workspaceView.objectMetadataId,
|
||||||
|
type: workspaceView.type,
|
||||||
|
key: workspaceView.key,
|
||||||
|
icon: workspaceView.icon,
|
||||||
|
position: workspaceView.position,
|
||||||
|
isCompact: workspaceView.isCompact,
|
||||||
|
openRecordIn:
|
||||||
|
workspaceView.openRecordIn === 'SIDE_PANEL'
|
||||||
|
? ViewOpenRecordIn.SIDE_PANEL
|
||||||
|
: ViewOpenRecordIn.RECORD_PAGE,
|
||||||
|
kanbanAggregateOperation: workspaceView.kanbanAggregateOperation,
|
||||||
|
kanbanAggregateOperationFieldMetadataId:
|
||||||
|
workspaceView.kanbanAggregateOperationFieldMetadataId,
|
||||||
|
workspaceId,
|
||||||
|
createdAt: new Date(workspaceView.createdAt),
|
||||||
|
updatedAt: new Date(workspaceView.updatedAt),
|
||||||
|
deletedAt: workspaceView.deletedAt
|
||||||
|
? new Date(workspaceView.deletedAt)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.coreViewRepository.save(coreView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateCoreView(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceView: ViewWorkspaceEntity,
|
||||||
|
diff?: Partial<ObjectRecordDiff<ViewWorkspaceEntity>>,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!diff || Object.keys(diff).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = this.parseUpdateDataFromDiff(diff);
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length > 0) {
|
||||||
|
await this.coreViewRepository.update(
|
||||||
|
{ id: workspaceView.id, workspaceId },
|
||||||
|
updateData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteCoreView(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceView: ViewWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewRepository.softDelete({
|
||||||
|
id: workspaceView.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyCoreView(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceView: ViewWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewRepository.delete({
|
||||||
|
id: workspaceView.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreCoreView(
|
||||||
|
workspaceId: string,
|
||||||
|
workspaceView: ViewWorkspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.coreViewRepository.restore({
|
||||||
|
id: workspaceView.id,
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { ViewFilterValue } from 'src/engine/metadata-modules/view/types/view-filter-value.type';
|
||||||
|
|
||||||
|
export const transformViewFilterWorkspaceValueToCoreValue = (
|
||||||
|
value: string,
|
||||||
|
): ViewFilterValue => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,11 +1,52 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||||
|
import { ViewField } from 'src/engine/metadata-modules/view/view-field.entity';
|
||||||
|
import { ViewFilterGroup } from 'src/engine/metadata-modules/view/view-filter-group.entity';
|
||||||
|
import { ViewFilter } from 'src/engine/metadata-modules/view/view-filter.entity';
|
||||||
|
import { ViewGroup } from 'src/engine/metadata-modules/view/view-group.entity';
|
||||||
|
import { ViewSort } from 'src/engine/metadata-modules/view/view-sort.entity';
|
||||||
|
import { View } from 'src/engine/metadata-modules/view/view.entity';
|
||||||
|
import { ViewFieldListener } from 'src/modules/view/listeners/view-field.listener';
|
||||||
|
import { ViewFilterGroupListener } from 'src/modules/view/listeners/view-filter-group.listener';
|
||||||
|
import { ViewFilterListener } from 'src/modules/view/listeners/view-filter.listener';
|
||||||
|
import { ViewGroupListener } from 'src/modules/view/listeners/view-group.listener';
|
||||||
|
import { ViewSortListener } from 'src/modules/view/listeners/view-sort.listener';
|
||||||
|
import { ViewListener } from 'src/modules/view/listeners/view.listener';
|
||||||
|
import { ViewDeleteOnePreQueryHook } from 'src/modules/view/pre-hooks/view-delete-one.pre-query.hook';
|
||||||
|
import { ViewFieldSyncService } from 'src/modules/view/services/view-field-sync.service';
|
||||||
|
import { ViewFilterGroupSyncService } from 'src/modules/view/services/view-filter-group-sync.service';
|
||||||
|
import { ViewFilterSyncService } from 'src/modules/view/services/view-filter-sync.service';
|
||||||
|
import { ViewGroupSyncService } from 'src/modules/view/services/view-group-sync.service';
|
||||||
|
import { ViewSortSyncService } from 'src/modules/view/services/view-sort-sync.service';
|
||||||
|
import { ViewSyncService } from 'src/modules/view/services/view-sync.service';
|
||||||
import { ViewService } from 'src/modules/view/services/view.service';
|
import { ViewService } from 'src/modules/view/services/view.service';
|
||||||
|
|
||||||
import { ViewDeleteOnePreQueryHook } from './pre-hooks/view-delete-one.pre-query.hook';
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
providers: [ViewService, ViewDeleteOnePreQueryHook],
|
TypeOrmModule.forFeature(
|
||||||
|
[View, ViewField, ViewFilter, ViewFilterGroup, ViewGroup, ViewSort],
|
||||||
|
'core',
|
||||||
|
),
|
||||||
|
FeatureFlagModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ViewService,
|
||||||
|
ViewDeleteOnePreQueryHook,
|
||||||
|
ViewSyncService,
|
||||||
|
ViewFieldSyncService,
|
||||||
|
ViewFilterSyncService,
|
||||||
|
ViewFilterGroupSyncService,
|
||||||
|
ViewGroupSyncService,
|
||||||
|
ViewSortSyncService,
|
||||||
|
ViewListener,
|
||||||
|
ViewFieldListener,
|
||||||
|
ViewFilterListener,
|
||||||
|
ViewFilterGroupListener,
|
||||||
|
ViewGroupListener,
|
||||||
|
ViewSortListener,
|
||||||
|
],
|
||||||
exports: [ViewService],
|
exports: [ViewService],
|
||||||
})
|
})
|
||||||
export class ViewModule {}
|
export class ViewModule {}
|
||||||
|
|||||||
@ -10,10 +10,12 @@ export enum ViewExceptionCode {
|
|||||||
VIEW_NOT_FOUND = 'VIEW_NOT_FOUND',
|
VIEW_NOT_FOUND = 'VIEW_NOT_FOUND',
|
||||||
CANNOT_DELETE_INDEX_VIEW = 'CANNOT_DELETE_INDEX_VIEW',
|
CANNOT_DELETE_INDEX_VIEW = 'CANNOT_DELETE_INDEX_VIEW',
|
||||||
METHOD_NOT_IMPLEMENTED = 'METHOD_NOT_IMPLEMENTED',
|
METHOD_NOT_IMPLEMENTED = 'METHOD_NOT_IMPLEMENTED',
|
||||||
|
CORE_VIEW_SYNC_ERROR = 'CORE_VIEW_SYNC_ERROR',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ViewExceptionMessage {
|
export enum ViewExceptionMessage {
|
||||||
VIEW_NOT_FOUND = 'View not found',
|
VIEW_NOT_FOUND = 'View not found',
|
||||||
CANNOT_DELETE_INDEX_VIEW = 'Cannot delete index view',
|
CANNOT_DELETE_INDEX_VIEW = 'Cannot delete index view',
|
||||||
METHOD_NOT_IMPLEMENTED = 'Method not implemented',
|
METHOD_NOT_IMPLEMENTED = 'Method not implemented',
|
||||||
|
CORE_VIEW_SYNC_ERROR = 'Failed to sync view data to core',
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user