## Summary - Migrates `LogicFunctionModule`, `CodeInterpreterModule`, and `CaptchaModule` from the `forRootAsync` + injection token pattern to the `DriverFactoryBase` lazy-loading pattern (matching `EmailModule` and `FileStorageModule`) - Fixes #18724 where `LOGIC_FUNCTION_TYPE` was not respected in worker processes because the driver was created at module boot time before the DB config cache was loaded - Removes `isEnvOnly` from `LOGIC_FUNCTION_TYPE`, `CODE_INTERPRETER_TYPE`, `CAPTCHA_DRIVER`, `IS_MULTIWORKSPACE_ENABLED`, and `FRONTEND_URL` — these can now be safely configured via the database at runtime ## How it works Each migrated module now uses a `DriverFactory` (extending `DriverFactoryBase`) instead of a module-level async factory + Symbol injection token: 1. **Lazy creation**: `getCurrentDriver()` creates the driver on first call, after `DatabaseConfigDriver.onModuleInit()` has loaded the DB cache 2. **Auto-recreation**: If config changes in the DB, the next `getCurrentDriver()` call detects the key mismatch and creates a new driver instance 3. **Unified config**: Both server and worker read from the same database — driver config only needs to be set once ### Files deleted (old pattern) - `logic-function-module.factory.ts`, `logic-function-drivers.module.ts`, `logic-function-driver.constants.ts` - `code-interpreter-module.factory.ts` - `captcha.module-factory.ts`, `captcha-driver.constants.ts` ### Files created (new pattern) - `logic-function-driver.factory.ts` - `code-interpreter-driver.factory.ts` - `captcha-driver.factory.ts` Net: **-150 lines** ## Test plan - [x] `npx nx typecheck twenty-server` passes - [x] `npx nx lint:diff-with-main twenty-server` passes - [ ] Integration tests pass (`npx nx run twenty-server:test:integration:with-db-reset`) - [ ] Verify logic functions execute in workflow runs (the original bug) - [ ] Verify code interpreter works in workflow code steps - [ ] Verify captcha validation works on sign-up (when captcha is configured) Made with [Cursor](https://cursor.com)
114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
import { APP_FILTER } from '@nestjs/core';
|
|
import { type NestExpressApplication } from '@nestjs/platform-express';
|
|
import {
|
|
Test,
|
|
type TestingModule,
|
|
type TestingModuleBuilder,
|
|
} from '@nestjs/testing';
|
|
|
|
import bytes from 'bytes';
|
|
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
|
|
|
import { AppModule } from 'src/app.module';
|
|
import { CommandModule } from 'src/command/command.module';
|
|
import { settings } from 'src/engine/constants/settings';
|
|
import { StripeSDKMockService } from 'src/engine/core-modules/billing/stripe/stripe-sdk/mocks/stripe-sdk-mock.service';
|
|
import { StripeSDKService } from 'src/engine/core-modules/billing/stripe/stripe-sdk/services/stripe-sdk.service';
|
|
import { CaptchaDriverFactory } from 'src/engine/core-modules/captcha/captcha-driver.factory';
|
|
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
|
import { ExceptionHandlerMockService } from 'src/engine/core-modules/exception-handler/mocks/exception-handler-mock.service';
|
|
import { MockedUnhandledExceptionFilter } from 'src/engine/core-modules/exception-handler/mocks/mock-unhandled-exception.filter';
|
|
import { SyncDriver } from 'src/engine/core-modules/message-queue/drivers/sync.driver';
|
|
import { JobsModule } from 'src/engine/core-modules/message-queue/jobs.module';
|
|
import { QUEUE_DRIVER } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
|
import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
|
|
|
|
interface TestingModuleCreatePreHook {
|
|
(moduleBuilder: TestingModuleBuilder): TestingModuleBuilder;
|
|
}
|
|
|
|
/**
|
|
* Hook for adding items to nest application
|
|
*/
|
|
export type TestingAppCreatePreHook = (
|
|
app: NestExpressApplication,
|
|
) => Promise<void>;
|
|
|
|
// Shared SyncDriver instance for all queues in tests
|
|
// This enables synchronous processing of jobs during integration tests
|
|
const syncDriver = new SyncDriver();
|
|
|
|
/**
|
|
* Sets basic integration testing module of app
|
|
*/
|
|
export const createApp = async (
|
|
config: {
|
|
moduleBuilderHook?: TestingModuleCreatePreHook;
|
|
appInitHook?: TestingAppCreatePreHook;
|
|
} = {},
|
|
): Promise<NestExpressApplication> => {
|
|
const stripeSDKMockService = new StripeSDKMockService();
|
|
const mockExceptionHandlerService = new ExceptionHandlerMockService();
|
|
let moduleBuilder: TestingModuleBuilder = Test.createTestingModule({
|
|
imports: [
|
|
AppModule,
|
|
CommandModule,
|
|
JobsModule,
|
|
MessageQueueModule.registerExplorer(),
|
|
],
|
|
providers: [
|
|
{
|
|
provide: APP_FILTER,
|
|
useClass: MockedUnhandledExceptionFilter,
|
|
},
|
|
],
|
|
})
|
|
.overrideProvider(StripeSDKService)
|
|
.useValue(stripeSDKMockService)
|
|
.overrideProvider(ExceptionHandlerService)
|
|
.useValue(mockExceptionHandlerService)
|
|
.overrideProvider(CaptchaDriverFactory)
|
|
.useValue({
|
|
getCurrentDriver: () => ({
|
|
validate: async () => ({ success: true }),
|
|
}),
|
|
})
|
|
.overrideProvider(QUEUE_DRIVER)
|
|
.useValue(syncDriver);
|
|
|
|
if (config.moduleBuilderHook) {
|
|
moduleBuilder = config.moduleBuilderHook(moduleBuilder);
|
|
}
|
|
|
|
const moduleFixture: TestingModule = await moduleBuilder.compile();
|
|
|
|
const app = moduleFixture.createNestApplication<NestExpressApplication>({
|
|
rawBody: true,
|
|
cors: true,
|
|
});
|
|
|
|
app.use(
|
|
'/graphql',
|
|
graphqlUploadExpress({
|
|
maxFieldSize: bytes(settings.storage.maxFileSize)!,
|
|
maxFiles: 10,
|
|
}),
|
|
);
|
|
|
|
app.use(
|
|
'/metadata',
|
|
graphqlUploadExpress({
|
|
maxFieldSize: bytes(settings.storage.maxFileSize)!,
|
|
maxFiles: 10,
|
|
}),
|
|
);
|
|
|
|
if (config.appInitHook) {
|
|
await config.appInitHook(app);
|
|
}
|
|
|
|
await app.init();
|
|
|
|
return app;
|
|
};
|