Add db event trigger and cron trigger to migration v2 (#14772)

## Context
Add DB events triggers and Cron triggers to migration v2 builder/runner
(and adding corresponding services/resolvers)
This commit is contained in:
Weiko
2025-09-30 11:14:18 +02:00
committed by GitHub
parent 123202df41
commit 68b21d2942
82 changed files with 3340 additions and 57 deletions
@@ -16,4 +16,6 @@ export const EMPTY_ORCHESTRATOR_ACTIONS_REPORT = {
viewField: emptyCreatedDeletedUpdated,
fieldMetadata: emptyCreatedDeletedUpdated,
serverlessFunction: emptyCreatedDeletedUpdated,
databaseEventTrigger: emptyCreatedDeletedUpdated,
cronTrigger: emptyCreatedDeletedUpdated,
} as const satisfies OrchestratorActionsReport;
@@ -11,6 +11,8 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
invalidViewField: 0,
invalidIndex: 0,
invalidServerlessFunction: 0,
invalidDatabaseEventTrigger: 0,
invalidCronTrigger: 0,
totalErrors: 0,
},
errors: {
@@ -19,6 +21,8 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
view: [],
viewField: [],
serverlessFunction: [],
databaseEventTrigger: [],
cronTrigger: [],
},
};
@@ -11,6 +11,8 @@ import {
WorkspaceMigrationOrchestratorFailedResult,
WorkspaceMigrationOrchestratorSuccessfulResult,
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service';
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
@@ -29,6 +31,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService,
private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService,
private readonly workspaceMigrationV2ServerlessFunctionActionsBuilderService: WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
private readonly workspaceMigrationV2DatabaseEventTriggerActionsBuilderService: WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
private readonly workspaceMigrationV2CronTriggerActionsBuilderService: WorkspaceMigrationV2CronTriggerActionsBuilderService,
) {}
private setupOptimisticCache({
@@ -80,6 +84,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
viewField: [],
index: [],
serverlessFunction: [],
databaseEventTrigger: [],
cronTrigger: [],
};
const optimisticAllFlatEntityMaps = this.setupOptimisticCache({
@@ -92,6 +98,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
flatViewMaps,
flatIndexMaps,
flatServerlessFunctionMaps,
flatDatabaseEventTriggerMaps,
flatCronTriggerMaps,
} = fromToAllFlatEntityMaps;
if (isDefined(flatObjectMetadataMaps)) {
@@ -222,6 +230,59 @@ export class WorkspaceMigrationBuildOrchestratorService {
}
}
if (isDefined(flatDatabaseEventTriggerMaps)) {
const {
from: fromFlatDatabaseEventTriggerMaps,
to: toFlatDatabaseEventTriggerMaps,
} = flatDatabaseEventTriggerMaps;
const databaseEventTriggerResult =
await this.workspaceMigrationV2DatabaseEventTriggerActionsBuilderService.validateAndBuild(
{
from: fromFlatDatabaseEventTriggerMaps,
to: toFlatDatabaseEventTriggerMaps,
buildOptions,
dependencyOptimisticFlatEntityMaps: {} as AllFlatEntityMaps,
},
);
optimisticAllFlatEntityMaps.flatDatabaseEventTriggerMaps =
databaseEventTriggerResult.optimisticFlatEntityMaps;
if (databaseEventTriggerResult.status === 'fail') {
orchestratorFailureReport.databaseEventTrigger.push(
...databaseEventTriggerResult.errors,
);
} else {
orchestratorActionsReport.databaseEventTrigger =
databaseEventTriggerResult.actions;
}
}
if (isDefined(flatCronTriggerMaps)) {
const { from: fromFlatCronTriggerMaps, to: toFlatCronTriggerMaps } =
flatCronTriggerMaps;
const cronTriggerResult =
await this.workspaceMigrationV2CronTriggerActionsBuilderService.validateAndBuild(
{
from: fromFlatCronTriggerMaps,
to: toFlatCronTriggerMaps,
buildOptions,
dependencyOptimisticFlatEntityMaps: {} as AllFlatEntityMaps,
},
);
optimisticAllFlatEntityMaps.flatCronTriggerMaps =
cronTriggerResult.optimisticFlatEntityMaps;
if (cronTriggerResult.status === 'fail') {
orchestratorFailureReport.cronTrigger.push(...cronTriggerResult.errors);
} else {
orchestratorActionsReport.cronTrigger = cronTriggerResult.actions;
}
}
const allErrors = Object.values(orchestratorFailureReport);
if (allErrors.some((report) => report.length > 0)) {
@@ -266,6 +327,18 @@ export class WorkspaceMigrationBuildOrchestratorService {
...orchestratorActionsReport.serverlessFunction.created,
...orchestratorActionsReport.serverlessFunction.updated,
///
// Database event triggers
...orchestratorActionsReport.databaseEventTrigger.deleted,
...orchestratorActionsReport.databaseEventTrigger.created,
...orchestratorActionsReport.databaseEventTrigger.updated,
///
// Cron triggers
...orchestratorActionsReport.cronTrigger.deleted,
...orchestratorActionsReport.cronTrigger.created,
...orchestratorActionsReport.cronTrigger.updated,
///
],
workspaceId,
},
@@ -0,0 +1,154 @@
import { Injectable } from '@nestjs/common';
import { FlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger.type';
import { compareTwoFlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/utils/compare-two-flat-cron-trigger.util';
import {
FlatEntityUpdateValidationArgs,
FlatEntityValidationArgs,
FlatEntityValidationReturnType,
WorkspaceEntityMigrationBuilderV2Service,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
import {
UpdateCronTriggerAction,
WorkspaceMigrationCronTriggerActionV2,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
import {
CronTriggerRelatedFlatEntityMaps,
FlatCronTriggerValidatorService,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
@Injectable()
export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
FlatCronTrigger,
WorkspaceMigrationCronTriggerActionV2,
CronTriggerRelatedFlatEntityMaps
> {
constructor(
private readonly flatCronTriggerValidatorService: FlatCronTriggerValidatorService,
) {
super();
}
protected async validateFlatEntityCreation({
dependencyOptimisticFlatEntityMaps,
flatEntityToValidate: flatCronTriggerToValidate,
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
}: FlatEntityValidationArgs<
FlatCronTrigger,
CronTriggerRelatedFlatEntityMaps
>): Promise<
FlatEntityValidationReturnType<
WorkspaceMigrationCronTriggerActionV2,
FlatCronTrigger
>
> {
const validationResult =
await this.flatCronTriggerValidatorService.validateFlatCronTriggerCreation(
{
flatCronTriggerToValidate,
optimisticFlatCronTriggerMaps,
dependencyOptimisticFlatEntityMaps,
},
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
return {
status: 'success',
action: {
type: 'create_cron_trigger',
cronTrigger: flatCronTriggerToValidate,
},
};
}
protected async validateFlatEntityDeletion({
dependencyOptimisticFlatEntityMaps,
flatEntityToValidate: flatCronTriggerToValidate,
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
}: FlatEntityValidationArgs<
FlatCronTrigger,
CronTriggerRelatedFlatEntityMaps
>): Promise<
FlatEntityValidationReturnType<
WorkspaceMigrationCronTriggerActionV2,
FlatCronTrigger
>
> {
const validationResult =
this.flatCronTriggerValidatorService.validateFlatCronTriggerDeletion({
flatCronTriggerToValidate,
optimisticFlatCronTriggerMaps,
dependencyOptimisticFlatEntityMaps,
});
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
return {
status: 'success',
action: {
type: 'delete_cron_trigger',
cronTriggerId: flatCronTriggerToValidate.id,
},
};
}
protected async validateFlatEntityUpdate({
dependencyOptimisticFlatEntityMaps,
flatEntityUpdate: { from: fromFlatCronTrigger, to: toFlatCronTrigger },
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
}: FlatEntityUpdateValidationArgs<
FlatCronTrigger,
CronTriggerRelatedFlatEntityMaps
>): Promise<
| FlatEntityValidationReturnType<
WorkspaceMigrationCronTriggerActionV2,
FlatCronTrigger
>
| undefined
> {
const cronTriggerUpdatedProperties = compareTwoFlatCronTrigger({
fromFlatCronTrigger,
toFlatCronTrigger,
});
if (cronTriggerUpdatedProperties.length === 0) {
return undefined;
}
const validationResult =
this.flatCronTriggerValidatorService.validateFlatCronTriggerUpdate({
flatCronTriggerToValidate: toFlatCronTrigger,
optimisticFlatCronTriggerMaps,
dependencyOptimisticFlatEntityMaps,
});
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
const updateCronTriggerAction: UpdateCronTriggerAction = {
type: 'update_cron_trigger',
cronTriggerId: toFlatCronTrigger.id,
updates: cronTriggerUpdatedProperties,
};
return {
status: 'success',
action: updateCronTriggerAction,
};
}
}
@@ -0,0 +1,162 @@
import { Injectable } from '@nestjs/common';
import { FlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger.type';
import { compareTwoFlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/utils/compare-two-flat-database-event-trigger.util';
import {
FlatEntityUpdateValidationArgs,
FlatEntityValidationArgs,
FlatEntityValidationReturnType,
WorkspaceEntityMigrationBuilderV2Service,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
import {
UpdateDatabaseEventTriggerAction,
WorkspaceMigrationDatabaseEventTriggerActionV2,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
import {
DatabaseEventTriggerRelatedFlatEntityMaps,
FlatDatabaseEventTriggerValidatorService,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
@Injectable()
export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
FlatDatabaseEventTrigger,
WorkspaceMigrationDatabaseEventTriggerActionV2,
DatabaseEventTriggerRelatedFlatEntityMaps
> {
constructor(
private readonly flatDatabaseEventTriggerValidatorService: FlatDatabaseEventTriggerValidatorService,
) {
super();
}
protected async validateFlatEntityCreation({
dependencyOptimisticFlatEntityMaps,
flatEntityToValidate: flatDatabaseEventTriggerToValidate,
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
}: FlatEntityValidationArgs<
FlatDatabaseEventTrigger,
DatabaseEventTriggerRelatedFlatEntityMaps
>): Promise<
FlatEntityValidationReturnType<
WorkspaceMigrationDatabaseEventTriggerActionV2,
FlatDatabaseEventTrigger
>
> {
const validationResult =
await this.flatDatabaseEventTriggerValidatorService.validateFlatDatabaseEventTriggerCreation(
{
flatDatabaseEventTriggerToValidate,
optimisticFlatDatabaseEventTriggerMaps,
dependencyOptimisticFlatEntityMaps,
},
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
return {
status: 'success',
action: {
type: 'create_database_event_trigger',
databaseEventTrigger: flatDatabaseEventTriggerToValidate,
},
};
}
protected async validateFlatEntityDeletion({
dependencyOptimisticFlatEntityMaps,
flatEntityToValidate: flatDatabaseEventTriggerToValidate,
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
}: FlatEntityValidationArgs<
FlatDatabaseEventTrigger,
DatabaseEventTriggerRelatedFlatEntityMaps
>): Promise<
FlatEntityValidationReturnType<
WorkspaceMigrationDatabaseEventTriggerActionV2,
FlatDatabaseEventTrigger
>
> {
const validationResult =
this.flatDatabaseEventTriggerValidatorService.validateFlatDatabaseEventTriggerDeletion(
{
flatDatabaseEventTriggerToValidate,
optimisticFlatDatabaseEventTriggerMaps,
dependencyOptimisticFlatEntityMaps,
},
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
return {
status: 'success',
action: {
type: 'delete_database_event_trigger',
databaseEventTriggerId: flatDatabaseEventTriggerToValidate.id,
},
};
}
protected async validateFlatEntityUpdate({
dependencyOptimisticFlatEntityMaps,
flatEntityUpdate: {
from: fromFlatDatabaseEventTrigger,
to: toFlatDatabaseEventTrigger,
},
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
}: FlatEntityUpdateValidationArgs<
FlatDatabaseEventTrigger,
DatabaseEventTriggerRelatedFlatEntityMaps
>): Promise<
| FlatEntityValidationReturnType<
WorkspaceMigrationDatabaseEventTriggerActionV2,
FlatDatabaseEventTrigger
>
| undefined
> {
const databaseEventTriggerUpdatedProperties =
compareTwoFlatDatabaseEventTrigger({
fromFlatDatabaseEventTrigger,
toFlatDatabaseEventTrigger,
});
if (databaseEventTriggerUpdatedProperties.length === 0) {
return undefined;
}
const validationResult =
this.flatDatabaseEventTriggerValidatorService.validateFlatDatabaseEventTriggerUpdate(
{
flatDatabaseEventTriggerToValidate: toFlatDatabaseEventTrigger,
optimisticFlatDatabaseEventTriggerMaps,
dependencyOptimisticFlatEntityMaps,
},
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
const updateDatabaseEventTriggerAction: UpdateDatabaseEventTriggerAction = {
type: 'update_database_event_trigger',
databaseEventTriggerId: toFlatDatabaseEventTrigger.id,
updates: databaseEventTriggerUpdatedProperties,
};
return {
status: 'success',
action: updateDatabaseEventTriggerAction,
};
}
}
@@ -1,3 +1,5 @@
import { type WorkspaceMigrationCronTriggerActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
import { type WorkspaceMigrationDatabaseEventTriggerActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
import { type WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
import { type WorkspaceMigrationIndexActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-index-action-v2';
import { type WorkspaceMigrationObjectActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
@@ -11,7 +13,9 @@ export type WorkspaceMigrationActionV2 =
| WorkspaceMigrationIndexActionV2
| WorkspaceMigrationViewActionV2
| WorkspaceMigrationViewFieldActionV2
| WorkspaceMigrationServerlessFunctionActionV2;
| WorkspaceMigrationServerlessFunctionActionV2
| WorkspaceMigrationDatabaseEventTriggerActionV2
| WorkspaceMigrationCronTriggerActionV2;
export type WorkspaceMigrationActionTypeV2 = WorkspaceMigrationActionV2['type'];
export type ExtractAction<T extends WorkspaceMigrationActionTypeV2> = Extract<
@@ -0,0 +1,31 @@
import { type FlatCronTriggerPropertiesToCompare } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger-properties-to-compare.type';
import { type FlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger.type';
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
export type CreateCronTriggerAction = {
type: 'create_cron_trigger';
cronTrigger: FlatCronTrigger;
};
export type UpdateCronTriggerAction = {
type: 'update_cron_trigger';
cronTriggerId: string;
updates: Array<
{
[P in FlatCronTriggerPropertiesToCompare]: PropertyUpdate<
FlatCronTrigger,
P
>;
}[FlatCronTriggerPropertiesToCompare]
>;
};
export type DeleteCronTriggerAction = {
type: 'delete_cron_trigger';
cronTriggerId: string;
};
export type WorkspaceMigrationCronTriggerActionV2 =
| CreateCronTriggerAction
| UpdateCronTriggerAction
| DeleteCronTriggerAction;
@@ -0,0 +1,31 @@
import { type FlatDatabaseEventTriggerPropertiesToCompare } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger-properties-to-compare.type';
import { type FlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger.type';
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
export type CreateDatabaseEventTriggerAction = {
type: 'create_database_event_trigger';
databaseEventTrigger: FlatDatabaseEventTrigger;
};
export type UpdateDatabaseEventTriggerAction = {
type: 'update_database_event_trigger';
databaseEventTriggerId: string;
updates: Array<
{
[P in FlatDatabaseEventTriggerPropertiesToCompare]: PropertyUpdate<
FlatDatabaseEventTrigger,
P
>;
}[FlatDatabaseEventTriggerPropertiesToCompare]
>;
};
export type DeleteDatabaseEventTriggerAction = {
type: 'delete_database_event_trigger';
databaseEventTriggerId: string;
};
export type WorkspaceMigrationDatabaseEventTriggerActionV2 =
| CreateDatabaseEventTriggerAction
| UpdateDatabaseEventTriggerAction
| DeleteDatabaseEventTriggerAction;
@@ -0,0 +1,107 @@
import { Injectable } from '@nestjs/common';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
import { CronTriggerExceptionCode } from 'src/engine/metadata-modules/cron-trigger/exceptions/cron-trigger.exception';
import { FlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger.type';
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
export type CronTriggerRelatedFlatEntityMaps = Pick<
AllFlatEntityMaps,
'flatCronTriggerMaps'
>;
type CronTriggerValidationArgs = {
flatCronTriggerToValidate: FlatCronTrigger;
optimisticFlatCronTriggerMaps: FlatEntityMaps<FlatCronTrigger>;
dependencyOptimisticFlatEntityMaps: CronTriggerRelatedFlatEntityMaps;
};
// TODO: validate settings integrity
@Injectable()
export class FlatCronTriggerValidatorService {
constructor() {}
public validateFlatCronTriggerUpdate({
flatCronTriggerToValidate: updatedFlatCronTrigger,
optimisticFlatCronTriggerMaps,
}: CronTriggerValidationArgs): FailedFlatEntityValidation<FlatCronTrigger> {
const errors = [];
const existingFlatCronTrigger =
optimisticFlatCronTriggerMaps.byId[updatedFlatCronTrigger.id];
if (!isDefined(existingFlatCronTrigger)) {
errors.push({
code: CronTriggerExceptionCode.CRON_TRIGGER_NOT_FOUND,
message: t`Cron trigger not found`,
userFriendlyMessage: t`Cron trigger not found`,
});
}
return {
type: 'update_cron_trigger',
errors,
flatEntityMinimalInformation: {
id: updatedFlatCronTrigger.id,
},
};
}
public validateFlatCronTriggerDeletion({
flatCronTriggerToValidate: { id: cronTriggerIdToDelete },
optimisticFlatCronTriggerMaps,
}: CronTriggerValidationArgs): FailedFlatEntityValidation<FlatCronTrigger> {
const errors = [];
const existingFlatCronTrigger =
optimisticFlatCronTriggerMaps.byId[cronTriggerIdToDelete];
if (!isDefined(existingFlatCronTrigger)) {
errors.push({
code: CronTriggerExceptionCode.CRON_TRIGGER_NOT_FOUND,
message: t`Cron trigger not found`,
userFriendlyMessage: t`Cron trigger not found`,
});
}
return {
type: 'delete_cron_trigger',
errors,
flatEntityMinimalInformation: {
id: cronTriggerIdToDelete,
},
};
}
public async validateFlatCronTriggerCreation({
flatCronTriggerToValidate,
optimisticFlatCronTriggerMaps,
}: CronTriggerValidationArgs): Promise<
FailedFlatEntityValidation<FlatCronTrigger>
> {
const errors = [];
if (
isDefined(
optimisticFlatCronTriggerMaps.byId[flatCronTriggerToValidate.id],
)
) {
errors.push({
code: CronTriggerExceptionCode.CRON_TRIGGER_ALREADY_EXIST,
message: t`Cron trigger with same id already exists`,
userFriendlyMessage: t`Cron trigger already exists`,
});
}
return {
type: 'create_cron_trigger',
errors,
flatEntityMinimalInformation: {
id: flatCronTriggerToValidate.id,
},
};
}
}
@@ -0,0 +1,113 @@
import { Injectable } from '@nestjs/common';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
import { DatabaseEventTriggerExceptionCode } from 'src/engine/metadata-modules/database-event-trigger/exceptions/database-event-trigger.exception';
import { FlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger.type';
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
export type DatabaseEventTriggerRelatedFlatEntityMaps = Pick<
AllFlatEntityMaps,
'flatDatabaseEventTriggerMaps'
>;
type DatabaseEventTriggerValidationArgs = {
flatDatabaseEventTriggerToValidate: FlatDatabaseEventTrigger;
optimisticFlatDatabaseEventTriggerMaps: FlatEntityMaps<FlatDatabaseEventTrigger>;
dependencyOptimisticFlatEntityMaps: DatabaseEventTriggerRelatedFlatEntityMaps;
};
// TODO: validate settings integrity
@Injectable()
export class FlatDatabaseEventTriggerValidatorService {
constructor() {}
public validateFlatDatabaseEventTriggerUpdate({
flatDatabaseEventTriggerToValidate: updatedFlatDatabaseEventTrigger,
optimisticFlatDatabaseEventTriggerMaps,
}: DatabaseEventTriggerValidationArgs): FailedFlatEntityValidation<FlatDatabaseEventTrigger> {
const errors = [];
const existingFlatDatabaseEventTrigger =
optimisticFlatDatabaseEventTriggerMaps.byId[
updatedFlatDatabaseEventTrigger.id
];
if (!isDefined(existingFlatDatabaseEventTrigger)) {
errors.push({
code: DatabaseEventTriggerExceptionCode.DATABASE_EVENT_TRIGGER_NOT_FOUND,
message: t`Database event trigger not found`,
userFriendlyMessage: t`Database event trigger not found`,
});
}
return {
type: 'update_database_event_trigger',
errors,
flatEntityMinimalInformation: {
id: updatedFlatDatabaseEventTrigger.id,
},
};
}
public validateFlatDatabaseEventTriggerDeletion({
flatDatabaseEventTriggerToValidate: { id: databaseEventTriggerIdToDelete },
optimisticFlatDatabaseEventTriggerMaps,
}: DatabaseEventTriggerValidationArgs): FailedFlatEntityValidation<FlatDatabaseEventTrigger> {
const errors = [];
const existingFlatDatabaseEventTrigger =
optimisticFlatDatabaseEventTriggerMaps.byId[
databaseEventTriggerIdToDelete
];
if (!isDefined(existingFlatDatabaseEventTrigger)) {
errors.push({
code: DatabaseEventTriggerExceptionCode.DATABASE_EVENT_TRIGGER_NOT_FOUND,
message: t`Database event trigger not found`,
userFriendlyMessage: t`Database event trigger not found`,
});
}
return {
type: 'delete_database_event_trigger',
errors,
flatEntityMinimalInformation: {
id: databaseEventTriggerIdToDelete,
},
};
}
public async validateFlatDatabaseEventTriggerCreation({
flatDatabaseEventTriggerToValidate,
optimisticFlatDatabaseEventTriggerMaps,
}: DatabaseEventTriggerValidationArgs): Promise<
FailedFlatEntityValidation<FlatDatabaseEventTrigger>
> {
const errors = [];
if (
isDefined(
optimisticFlatDatabaseEventTriggerMaps.byId[
flatDatabaseEventTriggerToValidate.id
],
)
) {
errors.push({
code: DatabaseEventTriggerExceptionCode.DATABASE_EVENT_TRIGGER_ALREADY_EXIST,
message: t`Database event trigger with same id already exists`,
userFriendlyMessage: t`Database event trigger already exists`,
});
}
return {
type: 'create_database_event_trigger',
errors,
flatEntityMinimalInformation: {
id: flatDatabaseEventTriggerToValidate.id,
},
};
}
}
@@ -4,6 +4,8 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
@@ -11,6 +13,8 @@ import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
import { FlatCronTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
import { FlatDatabaseEventTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
@@ -29,9 +33,13 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-
WorkspaceMigrationV2ViewFieldActionsBuilderService,
WorkspaceMigrationV2IndexActionsBuilderService,
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
WorkspaceMigrationV2CronTriggerActionsBuilderService,
FlatViewValidatorService,
FlatViewFieldValidatorService,
FlatServerlessFunctionValidatorService,
FlatDatabaseEventTriggerValidatorService,
FlatCronTriggerValidatorService,
],
exports: [
WorkspaceMigrationBuilderV2Service,
@@ -39,6 +47,8 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-
WorkspaceMigrationV2IndexActionsBuilderService,
WorkspaceMigrationV2ViewFieldActionsBuilderService,
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
WorkspaceMigrationV2CronTriggerActionsBuilderService,
],
})
export class WorkspaceMigrationBuilderV2Module {}
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { CronTrigger } from 'src/engine/metadata-modules/cron-trigger/entities/cron-trigger.entity';
import { CreateCronTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
@Injectable()
export class CreateCronTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'create_cron_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<CreateCronTriggerAction>,
): Promise<void> {
const { action, queryRunner, workspaceId } = context;
const { cronTrigger } = action;
const cronTriggerRepository =
queryRunner.manager.getRepository<CronTrigger>(CronTrigger);
await cronTriggerRepository.insert({
...cronTrigger,
workspaceId,
});
}
}
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { CronTrigger } from 'src/engine/metadata-modules/cron-trigger/entities/cron-trigger.entity';
import { DeleteCronTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
@Injectable()
export class DeleteCronTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'delete_cron_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<DeleteCronTriggerAction>,
): Promise<void> {
const { action, queryRunner, workspaceId } = context;
const { cronTriggerId } = action;
const cronTriggerRepository =
queryRunner.manager.getRepository<CronTrigger>(CronTrigger);
await cronTriggerRepository.delete({
id: cronTriggerId,
workspaceId,
});
}
}
@@ -0,0 +1,32 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { CronTrigger } from 'src/engine/metadata-modules/cron-trigger/entities/cron-trigger.entity';
import { UpdateCronTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
import { fromWorkspaceMigrationUpdateActionToPartialEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-workspace-migration-update-action-to-partial-field-or-object-entity.util';
@Injectable()
export class UpdateCronTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'update_cron_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<UpdateCronTriggerAction>,
): Promise<void> {
const { action, queryRunner } = context;
const { cronTriggerId } = action;
const cronTriggerRepository =
queryRunner.manager.getRepository<CronTrigger>(CronTrigger);
await cronTriggerRepository.update(
cronTriggerId,
fromWorkspaceMigrationUpdateActionToPartialEntity(action),
);
}
}
@@ -0,0 +1,33 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { DatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/entities/database-event-trigger.entity';
import { CreateDatabaseEventTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
@Injectable()
export class CreateDatabaseEventTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'create_database_event_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<CreateDatabaseEventTriggerAction>,
): Promise<void> {
const { action, queryRunner, workspaceId } = context;
const { databaseEventTrigger } = action;
const databaseEventTriggerRepository =
queryRunner.manager.getRepository<DatabaseEventTrigger>(
DatabaseEventTrigger,
);
await databaseEventTriggerRepository.insert({
...databaseEventTrigger,
workspaceId,
});
}
}
@@ -0,0 +1,33 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { DatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/entities/database-event-trigger.entity';
import { DeleteDatabaseEventTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
@Injectable()
export class DeleteDatabaseEventTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'delete_database_event_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<DeleteDatabaseEventTriggerAction>,
): Promise<void> {
const { action, queryRunner, workspaceId } = context;
const { databaseEventTriggerId } = action;
const databaseEventTriggerRepository =
queryRunner.manager.getRepository<DatabaseEventTrigger>(
DatabaseEventTrigger,
);
await databaseEventTriggerRepository.delete({
id: databaseEventTriggerId,
workspaceId,
});
}
}
@@ -0,0 +1,34 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
import { DatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/entities/database-event-trigger.entity';
import { UpdateDatabaseEventTriggerAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
import { fromWorkspaceMigrationUpdateActionToPartialEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-workspace-migration-update-action-to-partial-field-or-object-entity.util';
@Injectable()
export class UpdateDatabaseEventTriggerActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'update_database_event_trigger',
) {
constructor() {
super();
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerArgs<UpdateDatabaseEventTriggerAction>,
): Promise<void> {
const { action, queryRunner } = context;
const { databaseEventTriggerId } = action;
const databaseEventTriggerRepository =
queryRunner.manager.getRepository<DatabaseEventTrigger>(
DatabaseEventTrigger,
);
await databaseEventTriggerRepository.update(
databaseEventTriggerId,
fromWorkspaceMigrationUpdateActionToPartialEntity(action),
);
}
}
@@ -1,6 +1,12 @@
import { Module } from '@nestjs/common';
import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module';
import { CreateCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/create-cron-trigger-action-handler.service';
import { DeleteCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/delete-cron-trigger-action-handler.service';
import { UpdateCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/update-cron-trigger-action-handler.service';
import { CreateDatabaseEventTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/database-event-trigger/services/create-database-event-trigger-action-handler.service';
import { DeleteDatabaseEventTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/database-event-trigger/services/delete-database-event-trigger-action-handler.service';
import { UpdateDatabaseEventTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/database-event-trigger/services/update-database-event-trigger-action-handler.service';
import { CreateFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/create-field-action-handler.service';
import { DeleteFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/delete-field-action-handler.service';
import { UpdateFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service';
@@ -44,6 +50,14 @@ import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/wor
CreateServerlessFunctionActionHandlerService,
DeleteServerlessFunctionActionHandlerService,
UpdateServerlessFunctionActionHandlerService,
CreateDatabaseEventTriggerActionHandlerService,
DeleteDatabaseEventTriggerActionHandlerService,
UpdateDatabaseEventTriggerActionHandlerService,
CreateCronTriggerActionHandlerService,
DeleteCronTriggerActionHandlerService,
UpdateCronTriggerActionHandlerService,
],
})
export class WorkspaceSchemaMigrationRunnerActionHandlersModule {}