Rename serverlessFunction to logicFunction (#17494)

## Summary

Rename "Serverless Function" to "Logic Function" across the codebase for
clearer naming.

### Environment Variable Changes

| Old | New |
|-----|-----|
| `SERVERLESS_TYPE` | `LOGIC_FUNCTION_TYPE` |
| `SERVERLESS_LAMBDA_REGION` | `LOGIC_FUNCTION_LAMBDA_REGION` |
| `SERVERLESS_LAMBDA_ROLE` | `LOGIC_FUNCTION_LAMBDA_ROLE` |
| `SERVERLESS_LAMBDA_SUBHOSTING_URL` |
`LOGIC_FUNCTION_LAMBDA_SUBHOSTING_URL` |
| `SERVERLESS_LAMBDA_ACCESS_KEY_ID` |
`LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID` |
| `SERVERLESS_LAMBDA_SECRET_ACCESS_KEY` |
`LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY` |

### Breaking Changes

- Environment variables must be updated in production deployments
- Database migration renames `serverlessFunction` → `logicFunction`
tables
This commit is contained in:
Charles Bochet
2026-01-28 01:42:19 +01:00
committed by GitHub
parent 59d123d2b1
commit da6f1bbef3
351 changed files with 5054 additions and 5139 deletions
@@ -0,0 +1,15 @@
import { ArgsType, Field } from '@nestjs/graphql';
import { IsString } from 'class-validator';
import GraphQLJSON from 'graphql-type-json';
import { PackageJson } from 'twenty-shared/application';
@ArgsType()
export class CreateLogicFunctionLayerInput {
@Field(() => GraphQLJSON, { nullable: false })
packageJson: PackageJson;
@IsString()
@Field(() => String, { nullable: false })
yarnLock: string;
}
@@ -0,0 +1,23 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsDateString } from 'class-validator';
import { IDField } from '@ptc-org/nestjs-query-graphql';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
@ObjectType('LogicFunctionLayer')
export class LogicFunctionLayerDTO {
@IDField(() => UUIDScalarType)
id: string;
@IDField(() => UUIDScalarType, { nullable: true })
applicationId?: string;
@IsDateString()
@Field()
createdAt: Date;
@IsDateString()
@Field()
updatedAt: Date;
}
@@ -0,0 +1,43 @@
import { PackageJson } from 'twenty-shared/application';
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
Relation,
UpdateDateColumn,
} from 'typeorm';
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
@Entity('logicFunctionLayer')
export class LogicFunctionLayerEntity extends WorkspaceRelatedEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'jsonb', nullable: false })
packageJson: PackageJson;
@Column({ type: 'text', nullable: false })
yarnLock: string;
@Column({ type: 'text', nullable: false })
checksum: string;
@OneToMany(
() => LogicFunctionEntity,
(logicFunction) => logicFunction.logicFunctionLayer,
{
onDelete: 'RESTRICT',
},
)
logicFunctions: Relation<LogicFunctionEntity[]>;
@CreateDateColumn({ type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
}
@@ -0,0 +1,27 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { LogicFunctionLayerEntity } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.entity';
import { LogicFunctionLayerResolver } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.resolver';
import { LogicFunctionLayerService } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.service';
import { WorkspaceLogicFunctionLayerMapCacheService } from 'src/engine/metadata-modules/logic-function-layer/services/workspace-logic-function-layer-map-cache.service';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
@Module({
imports: [
PermissionsModule,
TypeOrmModule.forFeature([LogicFunctionLayerEntity]),
WorkspaceCacheModule,
],
providers: [
LogicFunctionLayerService,
LogicFunctionLayerResolver,
WorkspaceLogicFunctionLayerMapCacheService,
],
exports: [
LogicFunctionLayerService,
WorkspaceLogicFunctionLayerMapCacheService,
],
})
export class LogicFunctionLayerModule {}
@@ -0,0 +1,33 @@
import { UseGuards } from '@nestjs/common';
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { PermissionFlagType } from 'twenty-shared/constants';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { CreateLogicFunctionLayerInput } from 'src/engine/metadata-modules/logic-function-layer/dtos/create-logic-function-layer.input';
import { LogicFunctionLayerDTO } from 'src/engine/metadata-modules/logic-function-layer/dtos/logic-function-layer.dto';
import { LogicFunctionLayerService } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.service';
@UseGuards(WorkspaceAuthGuard)
@Resolver()
export class LogicFunctionLayerResolver {
constructor(
private readonly logicFunctionLayerService: LogicFunctionLayerService,
) {}
@Mutation(() => LogicFunctionLayerDTO)
@UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS))
async createOneLogicFunctionLayer(
@Args()
createLogicFunctionLayerInput: CreateLogicFunctionLayerInput,
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
) {
return this.logicFunctionLayerService.create(
createLogicFunctionLayerInput,
workspaceId,
);
}
}
@@ -0,0 +1,85 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import { LogicFunctionLayerEntity } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.entity';
import { CreateLogicFunctionLayerInput } from 'src/engine/metadata-modules/logic-function-layer/dtos/create-logic-function-layer.input';
import { getLastCommonLayerDependencies } from 'src/engine/core-modules/logic-function-executor/drivers/utils/get-last-common-layer-dependencies';
import { logicFunctionCreateHash } from 'src/engine/metadata-modules/logic-function/utils/logic-function-create-hash.utils';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
@Injectable()
export class LogicFunctionLayerService {
constructor(
@InjectRepository(LogicFunctionLayerEntity)
private readonly logicFunctionLayerRepository: Repository<LogicFunctionLayerEntity>,
private readonly workspaceCacheService: WorkspaceCacheService,
) {}
async create(
{ packageJson, yarnLock }: CreateLogicFunctionLayerInput,
workspaceId: string,
) {
const checksum = logicFunctionCreateHash(yarnLock);
const logicFunctionLayer = this.logicFunctionLayerRepository.create({
packageJson,
yarnLock,
checksum,
workspaceId,
});
const savedLayer =
await this.logicFunctionLayerRepository.save(logicFunctionLayer);
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
'logicFunctionLayerMaps',
]);
return savedLayer;
}
async update(
id: string,
data: QueryDeepPartialEntity<LogicFunctionLayerEntity>,
workspaceId: string,
) {
const checksum = data.yarnLock
? logicFunctionCreateHash(data.yarnLock as string)
: undefined;
const updateData = { ...data, ...(checksum && { checksum }) };
const result = await this.logicFunctionLayerRepository.update(
id,
updateData,
);
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
'logicFunctionLayerMaps',
]);
return result;
}
async createCommonLayerIfNotExist(workspaceId: string) {
const { packageJson, yarnLock } = await getLastCommonLayerDependencies();
const checksum = logicFunctionCreateHash(yarnLock);
const commonLayer = await this.logicFunctionLayerRepository.findOne({
where: {
checksum,
workspaceId,
},
});
if (isDefined(commonLayer)) {
return commonLayer;
}
return this.create({ packageJson, yarnLock }, workspaceId);
}
}
@@ -0,0 +1,47 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
import { LogicFunctionLayerEntity } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.entity';
import { type LogicFunctionLayerCacheMaps } from 'src/engine/metadata-modules/logic-function-layer/types/logic-function-layer-cache-maps.type';
import { fromLogicFunctionLayerEntityToFlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/utils/from-logic-function-layer-entity-to-flat-logic-function-layer.util';
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
@Injectable()
@WorkspaceCache('logicFunctionLayerMaps')
export class WorkspaceLogicFunctionLayerMapCacheService extends WorkspaceCacheProvider<LogicFunctionLayerCacheMaps> {
constructor(
@InjectRepository(LogicFunctionLayerEntity)
private readonly logicFunctionLayerRepository: Repository<LogicFunctionLayerEntity>,
) {
super();
}
async computeForCache(
workspaceId: string,
): Promise<LogicFunctionLayerCacheMaps> {
const logicFunctionLayerEntities =
await this.logicFunctionLayerRepository.find({
where: {
workspaceId,
},
});
const logicFunctionLayerMaps: LogicFunctionLayerCacheMaps = {
byId: {},
};
for (const entity of logicFunctionLayerEntities) {
const flatLogicFunctionLayer =
fromLogicFunctionLayerEntityToFlatLogicFunctionLayer(entity);
logicFunctionLayerMaps.byId[flatLogicFunctionLayer.id] =
flatLogicFunctionLayer;
}
return logicFunctionLayerMaps;
}
}
@@ -0,0 +1,4 @@
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type LogicFunctionLayerEntity } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.entity';
export type FlatLogicFunctionLayer = FlatEntityFrom<LogicFunctionLayerEntity>;
@@ -0,0 +1,5 @@
import { type FlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/types/flat-logic-function-layer.type';
export type LogicFunctionLayerCacheMaps = {
byId: Partial<Record<string, FlatLogicFunctionLayer>>;
};
@@ -0,0 +1,15 @@
import { type LogicFunctionLayerEntity } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.entity';
import { type FlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/types/flat-logic-function-layer.type';
export const fromLogicFunctionLayerEntityToFlatLogicFunctionLayer = (
entity: LogicFunctionLayerEntity,
): FlatLogicFunctionLayer => ({
id: entity.id,
packageJson: entity.packageJson,
yarnLock: entity.yarnLock,
checksum: entity.checksum,
workspaceId: entity.workspaceId,
createdAt: entity.createdAt.toISOString(),
updatedAt: entity.updatedAt.toISOString(),
logicFunctionIds: entity.logicFunctions?.map((lf) => lf.id) ?? [],
});