Migrate cron, databaseEventTrigger, httpRoute triggers to serverless functions (#17488)

## Summary

Migrates trigger entities (`CronTriggerEntity`,
`DatabaseEventTriggerEntity`, `RouteTriggerEntity`) into
`ServerlessFunctionEntity` by storing trigger settings as JSONB columns
directly on the serverless function. This simplifies the architecture
since these relationships were effectively one-to-one.

## Changes

### Schema Changes
- Added three new nullable JSONB columns to `ServerlessFunctionEntity`:
  - `cronTriggerSettings` - stores cron pattern
- `databaseEventTriggerSettings` - stores event name and updated fields
filter
- `httpRouteTriggerSettings` - stores path, HTTP method, auth
requirements, and forwarded headers

### Core Logic Updates
- `CronTriggerCronJob` - now queries `ServerlessFunctionEntity` directly
instead of `CronTriggerEntity`
- `CallDatabaseEventTriggerJobsJob` - now queries
`ServerlessFunctionEntity` directly
- `RouteTriggerService` - now queries `ServerlessFunctionEntity`
directly
- `ApplicationSyncService` - extracts trigger settings from manifest and
writes to serverless function
This commit is contained in:
Charles Bochet
2026-01-27 20:54:09 +01:00
committed by GitHub
parent e1f92bd951
commit a4499d21bb
110 changed files with 439 additions and 5193 deletions
@@ -1,7 +1,5 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { FLAT_CRON_TRIGGER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/cron-trigger/constants/flat-cron-trigger-editable-properties.constant';
import { FLAT_DATABASE_EVENT_TRIGGER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/database-event-trigger/constants/flat-database-event-trigger-editable-properties.constant';
import { FLAT_AGENT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-agent/constants/flat-agent-editable-properties.constant';
import { FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-command-menu-item/constants/flat-command-menu-item-editable-properties.constant';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
@@ -22,7 +20,6 @@ import { FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-
import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant';
import { FLAT_VIEW_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/flat-view-group-editable-properties.constant';
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view/constants/flat-view-editable-properties.constant';
import { FLAT_ROUTE_TRIGGER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/route-trigger/constants/flat-route-trigger-editable-properties.constant';
import { FLAT_SERVERLESS_FUNCTION_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/serverless-function/constants/flat-serverless-function-editable-properties.constant';
type OneFlatEntityConfiguration<T extends AllMetadataName> = {
@@ -88,15 +85,15 @@ export const ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY = {
propertiesToStringify: ['toolInputSchema', 'publishedVersions'],
},
cronTrigger: {
propertiesToCompare: [...FLAT_CRON_TRIGGER_EDITABLE_PROPERTIES],
propertiesToStringify: ['settings'],
propertiesToCompare: [],
propertiesToStringify: [],
},
databaseEventTrigger: {
propertiesToCompare: [...FLAT_DATABASE_EVENT_TRIGGER_EDITABLE_PROPERTIES],
propertiesToStringify: ['settings'],
propertiesToCompare: [],
propertiesToStringify: [],
},
routeTrigger: {
propertiesToCompare: [...FLAT_ROUTE_TRIGGER_EDITABLE_PROPERTIES],
propertiesToCompare: [],
propertiesToStringify: [],
},
viewFilter: {
@@ -3,8 +3,6 @@ import { type EntityTarget, type ObjectLiteral } from 'typeorm';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { CronTriggerEntity } from 'src/engine/metadata-modules/cron-trigger/entities/cron-trigger.entity';
import { DatabaseEventTriggerEntity } from 'src/engine/metadata-modules/database-event-trigger/entities/database-event-trigger.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
@@ -16,7 +14,6 @@ import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RouteTriggerEntity } from 'src/engine/metadata-modules/route-trigger/route-trigger.entity';
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
@@ -39,9 +36,9 @@ export const ALL_METADATA_ENTITY_BY_METADATA_NAME = {
view: ViewEntity,
index: IndexMetadataEntity,
pageLayoutTab: PageLayoutTabEntity,
routeTrigger: RouteTriggerEntity,
cronTrigger: CronTriggerEntity,
databaseEventTrigger: DatabaseEventTriggerEntity,
routeTrigger: ServerlessFunctionEntity,
cronTrigger: ServerlessFunctionEntity,
databaseEventTrigger: ServerlessFunctionEntity,
frontComponent: FrontComponentEntity,
fieldMetadata: FieldMetadataEntity,
pageLayout: PageLayoutEntity,
@@ -267,45 +267,29 @@ export const ALL_METADATA_RELATIONS = {
application: null,
serverlessFunctionLayer: null,
},
oneToMany: {
cronTriggers: { metadataName: 'cronTrigger' },
databaseEventTriggers: { metadataName: 'databaseEventTrigger' },
routeTriggers: { metadataName: 'routeTrigger' },
},
oneToMany: {},
},
cronTrigger: {
manyToOne: {
serverlessFunction: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'cronTriggerIds',
foreignKey: 'serverlessFunctionId',
},
workspace: null,
application: null,
serverlessFunctionLayer: null,
workspace: null,
},
oneToMany: {},
},
databaseEventTrigger: {
manyToOne: {
serverlessFunction: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'databaseEventTriggerIds',
foreignKey: 'serverlessFunctionId',
},
workspace: null,
application: null,
serverlessFunctionLayer: null,
workspace: null,
},
oneToMany: {},
},
routeTrigger: {
manyToOne: {
serverlessFunction: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'routeTriggerIds',
foreignKey: 'serverlessFunctionId',
},
workspace: null,
application: null,
serverlessFunctionLayer: null,
workspace: null,
},
oneToMany: {},
},
@@ -1,5 +1,3 @@
import { type FlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger.type';
import { type FlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger.type';
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
@@ -21,7 +19,6 @@ import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filte
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
import { type NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity';
import { type FlatRouteTrigger } from 'src/engine/metadata-modules/route-trigger/types/flat-route-trigger.type';
import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type';
import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type';
import { type FlatServerlessFunction } from 'src/engine/metadata-modules/serverless-function/types/flat-serverless-function.type';
@@ -35,16 +32,6 @@ import {
type DeleteCommandMenuItemAction,
type UpdateCommandMenuItemAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/command-menu-item/types/workspace-migration-command-menu-item-action.type';
import {
type CreateCronTriggerAction,
type DeleteCronTriggerAction,
type UpdateCronTriggerAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/cron-trigger/types/workspace-migration-cron-trigger-action.type';
import {
type CreateDatabaseEventTriggerAction,
type DeleteDatabaseEventTriggerAction,
type UpdateDatabaseEventTriggerAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/database-event-trigger/types/workspace-migration-database-event-trigger-action.type';
import {
type CreateFieldAction,
type DeleteFieldAction,
@@ -95,11 +82,6 @@ import {
type DeleteRoleAction,
type UpdateRoleAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role/types/workspace-migration-role-action.type';
import {
type CreateRouteTriggerAction,
type DeleteRouteTriggerAction,
type UpdateRouteTriggerAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/route-trigger/types/workspace-migration-route-trigger-action.type';
import {
type CreateRowLevelPermissionPredicateGroupAction,
type DeleteRowLevelPermissionPredicateGroupAction,
@@ -244,30 +226,30 @@ export type AllFlatEntityTypesByMetadataName = {
};
cronTrigger: {
actions: {
create: CreateCronTriggerAction;
update: UpdateCronTriggerAction;
delete: DeleteCronTriggerAction;
create: never;
update: never;
delete: never;
};
flatEntity: FlatCronTrigger;
entity: MetadataEntity<'cronTrigger'>;
flatEntity: FlatServerlessFunction;
entity: MetadataEntity<'serverlessFunction'>;
};
databaseEventTrigger: {
actions: {
create: CreateDatabaseEventTriggerAction;
update: UpdateDatabaseEventTriggerAction;
delete: DeleteDatabaseEventTriggerAction;
create: never;
update: never;
delete: never;
};
flatEntity: FlatDatabaseEventTrigger;
entity: MetadataEntity<'databaseEventTrigger'>;
flatEntity: FlatServerlessFunction;
entity: MetadataEntity<'serverlessFunction'>;
};
routeTrigger: {
actions: {
create: CreateRouteTriggerAction;
update: UpdateRouteTriggerAction;
delete: DeleteRouteTriggerAction;
create: never;
update: never;
delete: never;
};
flatEntity: FlatRouteTrigger;
entity: MetadataEntity<'routeTrigger'>;
flatEntity: FlatServerlessFunction;
entity: MetadataEntity<'serverlessFunction'>;
};
viewFilter: {
actions: {
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`getMetadataRelatedMetadataNames should return related metadata names for agent 1`] = `[]`;
@@ -8,17 +8,9 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for cronTrigger 1`] = `
[
"serverlessFunction",
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for cronTrigger 1`] = `[]`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for databaseEventTrigger 1`] = `
[
"serverlessFunction",
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for databaseEventTrigger 1`] = `[]`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for fieldMetadata 1`] = `
[
@@ -89,11 +81,7 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for routeTrigger 1`] = `
[
"serverlessFunction",
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for routeTrigger 1`] = `[]`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for rowLevelPermissionPredicate 1`] = `
[
@@ -113,13 +101,7 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for serverlessFunction 1`] = `
[
"cronTrigger",
"databaseEventTrigger",
"routeTrigger",
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for serverlessFunction 1`] = `[]`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for skill 1`] = `[]`;
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with children first (most manyToOne relations first) 1`] = `
[
@@ -8,24 +8,24 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
"pageLayoutWidget",
"viewField",
"commandMenuItem",
"cronTrigger",
"databaseEventTrigger",
"index",
"roleTarget",
"routeTrigger",
"rowLevelPermissionPredicateGroup",
"viewGroup",
"agent",
"cronTrigger",
"databaseEventTrigger",
"frontComponent",
"pageLayout",
"pageLayoutTab",
"routeTrigger",
"serverlessFunction",
"skill",
"view",
"viewFilterGroup",
"webhook",
"fieldMetadata",
"role",
"serverlessFunction",
"objectMetadata",
]
`;