Upload files instead ofsources (#17608)
This commit is contained in:
+65
-1
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import { dirname, join } from 'path';
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { build } from 'esbuild';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
getLogicFunctionBaseFolderPath,
|
||||
getRelativePathFromBase,
|
||||
} from 'src/engine/core-modules/logic-function/logic-function-build/utils/get-logic-function-base-folder-path.util';
|
||||
import { FlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/types/flat-logic-function-layer.type';
|
||||
|
||||
export type FunctionBuildParams = {
|
||||
flatLogicFunction: FlatLogicFunction;
|
||||
@@ -23,6 +25,64 @@ export type FunctionBuildParams = {
|
||||
export class LogicFunctionBuildService {
|
||||
constructor(private readonly fileStorageService: FileStorageService) {}
|
||||
|
||||
async hasLayerDependencies({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): Promise<boolean> {
|
||||
const packageJsonExists = await this.fileStorageService.checkFileExists_v2({
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'package.json',
|
||||
});
|
||||
|
||||
const yarnLockExists = await this.fileStorageService.checkFileExists_v2({
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'yarn.lock',
|
||||
});
|
||||
|
||||
return packageJsonExists && yarnLockExists;
|
||||
}
|
||||
|
||||
async uploadDependencies({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
await this.fileStorageService.writeFile_v2({
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'package.json',
|
||||
sourceFile: JSON.stringify(flatLogicFunctionLayer.packageJson, null, 2),
|
||||
mimeType: undefined,
|
||||
settings: {
|
||||
isTemporaryFile: false,
|
||||
toDelete: false,
|
||||
},
|
||||
});
|
||||
|
||||
await this.fileStorageService.writeFile_v2({
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'yarn.lock',
|
||||
sourceFile: flatLogicFunctionLayer.yarnLock,
|
||||
mimeType: undefined,
|
||||
settings: {
|
||||
isTemporaryFile: false,
|
||||
toDelete: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async isBuilt({
|
||||
flatLogicFunction,
|
||||
applicationUniversalIdentifier,
|
||||
@@ -38,7 +98,7 @@ export class LogicFunctionBuildService {
|
||||
async buildAndUpload({
|
||||
flatLogicFunction,
|
||||
applicationUniversalIdentifier,
|
||||
}: FunctionBuildParams): Promise<void> {
|
||||
}: FunctionBuildParams): Promise<{ checksum: string }> {
|
||||
const lambdaBuildDirectoryManager = new LambdaBuildDirectoryManager();
|
||||
|
||||
try {
|
||||
@@ -85,6 +145,10 @@ export class LogicFunctionBuildService {
|
||||
toDelete: false,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
checksum: crypto.createHash('md5').update(builtFile).digest('hex'),
|
||||
};
|
||||
} finally {
|
||||
await lambdaBuildDirectoryManager.clean();
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
var main = async (params) => {
|
||||
const { a, b } = params;
|
||||
const message = `Hello, input: ${a} and ${b}`;
|
||||
return { message };
|
||||
};
|
||||
export {
|
||||
main
|
||||
};
|
||||
+60
-15
@@ -29,7 +29,7 @@ import {
|
||||
} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-executor-driver.interface';
|
||||
|
||||
import { type FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { copyAndBuildDependencies } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-and-build-dependencies';
|
||||
import { copyYarnEngineAndBuildDependencies } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-engine-and-build-dependencies';
|
||||
import { copyExecutor } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor';
|
||||
import { createZipFile } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/create-zip-file';
|
||||
import {
|
||||
@@ -139,12 +139,43 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
|
||||
}
|
||||
|
||||
private getLayerName(flatLogicFunctionLayer: FlatLogicFunctionLayer) {
|
||||
return flatLogicFunctionLayer.checksum;
|
||||
return flatLogicFunctionLayer.yarnLockChecksum;
|
||||
}
|
||||
|
||||
private async createLayerIfNotExists(
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer,
|
||||
): Promise<string> {
|
||||
private async copyDependenciesInMemory({
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
inMemoryLayerFolderPath,
|
||||
}: {
|
||||
applicationUniversalIdentifier: string;
|
||||
workspaceId: string;
|
||||
inMemoryLayerFolderPath: string;
|
||||
}) {
|
||||
await Promise.all([
|
||||
this.fileStorageService.downloadFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'package.json',
|
||||
localPath: join(inMemoryLayerFolderPath, 'package.json'),
|
||||
}),
|
||||
this.fileStorageService.downloadFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'yarn.lock',
|
||||
localPath: join(inMemoryLayerFolderPath, 'yarn.lock'),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
private async createLayerIfNotExists({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): Promise<string> {
|
||||
const layerName = this.getLayerName(flatLogicFunctionLayer);
|
||||
|
||||
const listLayerParams: ListLayerVersionsCommandInput = {
|
||||
@@ -171,10 +202,12 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
|
||||
NODE_LAYER_SUBFOLDER,
|
||||
);
|
||||
|
||||
await copyAndBuildDependencies(
|
||||
nodeDependenciesFolder,
|
||||
flatLogicFunctionLayer,
|
||||
);
|
||||
await this.copyDependenciesInMemory({
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
inMemoryLayerFolderPath: nodeDependenciesFolder,
|
||||
});
|
||||
await copyYarnEngineAndBuildDependencies(nodeDependenciesFolder);
|
||||
|
||||
await createZipFile(sourceTemporaryDir, lambdaZipPath);
|
||||
|
||||
@@ -257,15 +290,23 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
|
||||
return false;
|
||||
}
|
||||
|
||||
private async build(
|
||||
flatLogicFunction: FlatLogicFunction,
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer,
|
||||
) {
|
||||
private async build({
|
||||
flatLogicFunction,
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunction: FlatLogicFunction;
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
if (await this.isAlreadyBuilt(flatLogicFunction, flatLogicFunctionLayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const layerArn = await this.createLayerIfNotExists(flatLogicFunctionLayer);
|
||||
const layerArn = await this.createLayerIfNotExists({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const lambdaBuildDirectoryManager = new LambdaBuildDirectoryManager();
|
||||
|
||||
@@ -317,7 +358,11 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
|
||||
payload,
|
||||
env,
|
||||
}: LogicFunctionExecuteParams): Promise<LogicFunctionExecuteResult> {
|
||||
await this.build(flatLogicFunction, flatLogicFunctionLayer);
|
||||
await this.build({
|
||||
flatLogicFunction,
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
await this.waitFunctionUpdates(flatLogicFunction);
|
||||
|
||||
|
||||
+57
-12
@@ -5,14 +5,14 @@ import { join } from 'path';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
type LogicFunctionExecutorDriver,
|
||||
type LogicFunctionExecuteParams,
|
||||
type LogicFunctionExecuteResult,
|
||||
type LogicFunctionExecutorDriver,
|
||||
} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-executor-driver.interface';
|
||||
|
||||
import { type FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { LOGIC_FUNCTION_EXECUTOR_TMPDIR_FOLDER } from 'src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-executor-tmpdir-folder';
|
||||
import { copyAndBuildDependencies } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-and-build-dependencies';
|
||||
import { copyYarnEngineAndBuildDependencies } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-engine-and-build-dependencies';
|
||||
import { ConsoleListener } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/intercept-console';
|
||||
import { LambdaBuildDirectoryManager } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/lambda-build-directory-manager';
|
||||
import { type FlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/types/flat-logic-function-layer.type';
|
||||
@@ -38,13 +38,44 @@ export class LocalDriver implements LogicFunctionExecutorDriver {
|
||||
) => {
|
||||
return join(
|
||||
LOGIC_FUNCTION_EXECUTOR_TMPDIR_FOLDER,
|
||||
flatLogicFunctionLayer.checksum,
|
||||
flatLogicFunctionLayer.yarnLockChecksum,
|
||||
);
|
||||
};
|
||||
|
||||
private async createLayerIfNotExists(
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer,
|
||||
) {
|
||||
private async copyDependenciesInMemory({
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
inMemoryLayerFolderPath,
|
||||
}: {
|
||||
applicationUniversalIdentifier: string;
|
||||
workspaceId: string;
|
||||
inMemoryLayerFolderPath: string;
|
||||
}) {
|
||||
await Promise.all([
|
||||
this.fileStorageService.downloadFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'package.json',
|
||||
localPath: join(inMemoryLayerFolderPath, 'package.json'),
|
||||
}),
|
||||
this.fileStorageService.downloadFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'yarn.lock',
|
||||
localPath: join(inMemoryLayerFolderPath, 'yarn.lock'),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
private async createLayerIfNotExists({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
const inMemoryLayerFolderPath = this.getInMemoryLayerFolderPath(
|
||||
flatLogicFunctionLayer,
|
||||
);
|
||||
@@ -52,17 +83,28 @@ export class LocalDriver implements LogicFunctionExecutorDriver {
|
||||
try {
|
||||
await fs.access(inMemoryLayerFolderPath);
|
||||
} catch {
|
||||
await copyAndBuildDependencies(
|
||||
await this.copyDependenciesInMemory({
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId: flatLogicFunctionLayer.workspaceId,
|
||||
inMemoryLayerFolderPath,
|
||||
flatLogicFunctionLayer,
|
||||
);
|
||||
});
|
||||
await copyYarnEngineAndBuildDependencies(inMemoryLayerFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
async delete() {}
|
||||
|
||||
private async build(flatLogicFunctionLayer: FlatLogicFunctionLayer) {
|
||||
await this.createLayerIfNotExists(flatLogicFunctionLayer);
|
||||
private async build({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
await this.createLayerIfNotExists({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
async execute({
|
||||
@@ -72,7 +114,10 @@ export class LocalDriver implements LogicFunctionExecutorDriver {
|
||||
payload,
|
||||
env,
|
||||
}: LogicFunctionExecuteParams): Promise<LogicFunctionExecuteResult> {
|
||||
await this.build(flatLogicFunctionLayer);
|
||||
await this.build({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
+1
-15
@@ -4,30 +4,16 @@ import { join } from 'path';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { getLayerDependenciesDirName } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/get-layer-dependencies-dir-name';
|
||||
import { type FlatLogicFunctionLayer } from 'src/engine/metadata-modules/logic-function-layer/types/flat-logic-function-layer.type';
|
||||
|
||||
const execFilePromise = promisify(execFile);
|
||||
|
||||
export const copyAndBuildDependencies = async (
|
||||
export const copyYarnEngineAndBuildDependencies = async (
|
||||
buildDirectory: string,
|
||||
flatLogicFunctionLayer: FlatLogicFunctionLayer,
|
||||
) => {
|
||||
await fs.mkdir(buildDirectory, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
const packageJson = flatLogicFunctionLayer.packageJson;
|
||||
|
||||
const yarnLock = flatLogicFunctionLayer.yarnLock;
|
||||
|
||||
await fs.writeFile(
|
||||
join(buildDirectory, 'package.json'),
|
||||
JSON.stringify(packageJson, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
await fs.writeFile(join(buildDirectory, 'yarn.lock'), yarnLock, 'utf8');
|
||||
|
||||
await fs.cp(getLayerDependenciesDirName('engine'), buildDirectory, {
|
||||
recursive: true,
|
||||
});
|
||||
+2
-4
@@ -1,13 +1,11 @@
|
||||
import fs from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
import { type PackageJson } from 'twenty-shared/application';
|
||||
|
||||
import { getLayerDependenciesDirName } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/get-layer-dependencies-dir-name';
|
||||
import { LAST_LAYER_VERSION } from 'src/engine/core-modules/logic-function/logic-function-drivers/layers/last-layer-version';
|
||||
|
||||
export type LayerDependencies = {
|
||||
packageJson: PackageJson;
|
||||
packageJson: string;
|
||||
yarnLock: string;
|
||||
};
|
||||
|
||||
@@ -20,5 +18,5 @@ export const getLastCommonLayerDependencies = async (
|
||||
fs.readFile(join(lastVersionLayerDirName, 'yarn.lock'), 'utf8'),
|
||||
]);
|
||||
|
||||
return { packageJson: JSON.parse(packageJson), yarnLock };
|
||||
return { packageJson, yarnLock };
|
||||
};
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ const getAllFiles = async (
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...(await getAllFiles(rootDir, fullPath, files)));
|
||||
await getAllFiles(rootDir, fullPath, files);
|
||||
} else {
|
||||
files.push({
|
||||
path: path.relative(rootDir, dir),
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.mod
|
||||
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { LogicFunctionLayerModule } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -22,6 +23,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
SubscriptionsModule,
|
||||
WorkspaceCacheModule,
|
||||
LogicFunctionBuildModule,
|
||||
LogicFunctionLayerModule,
|
||||
FileModule,
|
||||
TypeOrmModule.forFeature([LogicFunctionEntity]),
|
||||
],
|
||||
|
||||
+32
-20
@@ -6,7 +6,7 @@ import {
|
||||
DEFAULT_API_URL_NAME,
|
||||
} from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isDefined, isEmptyObject } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
@@ -35,6 +35,7 @@ import { SubscriptionChannel } from 'src/engine/subscriptions/enums/subscription
|
||||
import { SubscriptionService } from 'src/engine/subscriptions/subscription.service';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { cleanServerUrl } from 'src/utils/clean-server-url';
|
||||
import { LogicFunctionLayerService } from 'src/engine/core-modules/logic-function/logic-function-layer/services/logic-function-layer.service';
|
||||
|
||||
const MIN_TOKEN_EXPIRATION_IN_SECONDS = 5;
|
||||
|
||||
@@ -68,6 +69,7 @@ export class LogicFunctionExecutorService
|
||||
private readonly functionBuildService: LogicFunctionBuildService,
|
||||
private readonly subscriptionService: SubscriptionService,
|
||||
private readonly auditService: AuditService,
|
||||
private readonly logicFunctionLayerService: LogicFunctionLayerService,
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
@InjectRepository(LogicFunctionEntity)
|
||||
private readonly logicFunctionRepository: Repository<LogicFunctionEntity>,
|
||||
@@ -178,6 +180,19 @@ export class LogicFunctionExecutorService
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: remove when all logic functions are migrated
|
||||
if (
|
||||
!(await this.functionBuildService.hasLayerDependencies({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
}))
|
||||
) {
|
||||
await this.functionBuildService.uploadDependencies({
|
||||
flatLogicFunctionLayer,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!(await this.functionBuildService.isBuilt({
|
||||
flatLogicFunction,
|
||||
@@ -189,6 +204,7 @@ export class LogicFunctionExecutorService
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
}
|
||||
// END TODO
|
||||
|
||||
const resultLogicFunction = await this.callWithTimeout({
|
||||
callback: () =>
|
||||
@@ -291,30 +307,26 @@ export class LogicFunctionExecutorService
|
||||
async getAvailablePackages(logicFunctionId: string) {
|
||||
const logicFunction = await this.logicFunctionRepository.findOneOrFail({
|
||||
where: { id: logicFunctionId },
|
||||
relations: ['logicFunctionLayer'],
|
||||
relations: ['logicFunctionLayer', 'application'],
|
||||
});
|
||||
|
||||
const packageJson = logicFunction.logicFunctionLayer.packageJson;
|
||||
if (isEmptyObject(logicFunction.logicFunctionLayer.availablePackages)) {
|
||||
await this.logicFunctionLayerService.update(
|
||||
logicFunction.logicFunctionLayer.id,
|
||||
{},
|
||||
logicFunction.application.universalIdentifier,
|
||||
logicFunction.workspaceId,
|
||||
);
|
||||
|
||||
const yarnLock = logicFunction.logicFunctionLayer.yarnLock;
|
||||
|
||||
const packageVersionRegex = /^"([^@]+)@.*?":\n\s+version: (.+)$/gm;
|
||||
|
||||
const versions: Record<string, string> = {};
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = packageVersionRegex.exec(yarnLock)) !== null) {
|
||||
const packageName = match[1].split('@', 1)[0];
|
||||
const version = match[2];
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
if (packageJson.dependencies?.[packageName]) {
|
||||
versions[packageName] = version;
|
||||
}
|
||||
return (
|
||||
await this.logicFunctionRepository.findOneOrFail({
|
||||
where: { id: logicFunctionId },
|
||||
relations: ['logicFunctionLayer'],
|
||||
})
|
||||
).logicFunctionLayer.availablePackages;
|
||||
}
|
||||
|
||||
return versions;
|
||||
return logicFunction.logicFunctionLayer.availablePackages;
|
||||
}
|
||||
|
||||
private async throttleExecution(workspaceId: string) {
|
||||
|
||||
+123
-22
@@ -3,6 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { PackageJson } from 'type-fest';
|
||||
|
||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
@@ -11,6 +13,8 @@ import { CreateLogicFunctionLayerInput } from 'src/engine/metadata-modules/logic
|
||||
import { getLastCommonLayerDependencies } from 'src/engine/core-modules/logic-function/logic-function-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';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
|
||||
@Injectable()
|
||||
export class LogicFunctionLayerService {
|
||||
@@ -18,23 +22,34 @@ export class LogicFunctionLayerService {
|
||||
@InjectRepository(LogicFunctionLayerEntity)
|
||||
private readonly logicFunctionLayerRepository: Repository<LogicFunctionLayerEntity>,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
) {}
|
||||
|
||||
async create(
|
||||
{ packageJson, yarnLock }: CreateLogicFunctionLayerInput,
|
||||
{
|
||||
packageJsonChecksum,
|
||||
yarnLockChecksum,
|
||||
applicationUniversalIdentifier,
|
||||
}: CreateLogicFunctionLayerInput,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const checksum = logicFunctionCreateHash(yarnLock);
|
||||
|
||||
const logicFunctionLayer = this.logicFunctionLayerRepository.create({
|
||||
packageJson,
|
||||
yarnLock,
|
||||
checksum,
|
||||
packageJson: {}, // TODO: Delete when migration to Source files storage is done
|
||||
yarnLock: '', // TODO: Delete when migration to Source files storage is done
|
||||
packageJsonChecksum,
|
||||
yarnLockChecksum,
|
||||
workspaceId,
|
||||
} as Omit<LogicFunctionLayerEntity, 'workspace'>);
|
||||
|
||||
const availablePackages = await this.getAvailablePackages({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const savedLayer =
|
||||
await this.logicFunctionLayerRepository.save(logicFunctionLayer);
|
||||
const savedLayer = await this.logicFunctionLayerRepository.save({
|
||||
...logicFunctionLayer,
|
||||
availablePackages,
|
||||
});
|
||||
|
||||
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
|
||||
'logicFunctionLayerMaps',
|
||||
@@ -45,19 +60,19 @@ export class LogicFunctionLayerService {
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: QueryDeepPartialEntity<LogicFunctionLayerEntity>,
|
||||
data: QueryDeepPartialEntity<Omit<LogicFunctionLayerEntity, 'workspace'>>,
|
||||
applicationUniversalIdentifier: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const checksum = data.yarnLock
|
||||
? logicFunctionCreateHash(data.yarnLock as string)
|
||||
: undefined;
|
||||
const availablePackages = await this.getAvailablePackages({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const updateData = { ...data, ...(checksum && { checksum }) };
|
||||
|
||||
const result = await this.logicFunctionLayerRepository.update(
|
||||
id,
|
||||
updateData,
|
||||
);
|
||||
const result = await this.logicFunctionLayerRepository.update(id, {
|
||||
...data,
|
||||
availablePackages,
|
||||
});
|
||||
|
||||
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
|
||||
'logicFunctionLayerMaps',
|
||||
@@ -66,12 +81,45 @@ export class LogicFunctionLayerService {
|
||||
return result;
|
||||
}
|
||||
|
||||
async createCommonLayerIfNotExist(workspaceId: string) {
|
||||
async createCommonLayer({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
const { packageJson, yarnLock } = await getLastCommonLayerDependencies();
|
||||
const checksum = logicFunctionCreateHash(yarnLock);
|
||||
|
||||
await this.fileStorageService.writeFile_v2({
|
||||
sourceFile: packageJson,
|
||||
mimeType: undefined,
|
||||
fileFolder: FileFolder.Source,
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
resourcePath: 'package.json',
|
||||
settings: { isTemporaryFile: false, toDelete: false },
|
||||
});
|
||||
|
||||
await this.fileStorageService.writeFile_v2({
|
||||
sourceFile: yarnLock,
|
||||
mimeType: undefined,
|
||||
fileFolder: FileFolder.Source,
|
||||
applicationUniversalIdentifier,
|
||||
workspaceId,
|
||||
resourcePath: 'yarn.lock',
|
||||
settings: { isTemporaryFile: false, toDelete: false },
|
||||
});
|
||||
|
||||
const packageJsonChecksum = logicFunctionCreateHash(
|
||||
JSON.stringify(packageJson),
|
||||
);
|
||||
|
||||
const yarnLockChecksum = logicFunctionCreateHash(yarnLock);
|
||||
|
||||
const commonLayer = await this.logicFunctionLayerRepository.findOne({
|
||||
where: {
|
||||
checksum,
|
||||
yarnLockChecksum,
|
||||
packageJsonChecksum,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
@@ -80,6 +128,59 @@ export class LogicFunctionLayerService {
|
||||
return commonLayer;
|
||||
}
|
||||
|
||||
return this.create({ packageJson, yarnLock }, workspaceId);
|
||||
return this.create(
|
||||
{ packageJsonChecksum, yarnLockChecksum, applicationUniversalIdentifier },
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async getAvailablePackages({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
}) {
|
||||
const packageJson = JSON.parse(
|
||||
(
|
||||
await streamToBuffer(
|
||||
await this.fileStorageService.readFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'package.json',
|
||||
}),
|
||||
)
|
||||
).toString('utf-8'),
|
||||
) as PackageJson;
|
||||
|
||||
const yarnLock = (
|
||||
await streamToBuffer(
|
||||
await this.fileStorageService.readFile_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: 'yarn.lock',
|
||||
}),
|
||||
)
|
||||
).toString('utf-8');
|
||||
|
||||
const packageVersionRegex =
|
||||
/^"(@?[^@]+(?:\/[^@]+)?)@.*?":\n\s+version:\s*(.+)$/gm;
|
||||
|
||||
const versions: Record<string, string> = {};
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = packageVersionRegex.exec(yarnLock)) !== null) {
|
||||
const packageName = match[1];
|
||||
const version = match[2];
|
||||
|
||||
if (packageJson.dependencies?.[packageName]) {
|
||||
versions[packageName] = version;
|
||||
}
|
||||
}
|
||||
|
||||
return versions;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user