Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 7e259d75e8 fix: skip built file copy in duplicateOneWithSource when build is not up to date
https://sonarly.com/issue/19030?type=bug

Creating a draft from a workflow version fails when a CODE step references a logic function whose built file (.mjs) doesn't exist in S3, because `duplicateOneWithSource` unconditionally tries to copy both source and built files without checking `isBuildUpToDate`.

Fix: **What changed:**

1. **`logic-function-from-source.service.ts`**: In `duplicateOneWithSource()`, replaced the single `copyResources()` call with two separate calls: `copySourceResources()` (always) and `copyBuiltResources()` (only when `existingLogicFunction.isBuildUpToDate` is true). This mirrors the guard pattern already used in `executeOneFromSource()` at line 362.

2. **`logic-function-resource.service.ts`**: Added two new methods `copySourceResources()` and `copyBuiltResources()` that handle source and built file copying independently, with their own typed parameter interfaces. The existing `copyResources()` method is preserved (though no longer called) to avoid breaking any future code that might reference it.

**Why this works:** When a logic function is created with custom source code (via `input.source`), only the source file is uploaded — no built `.mjs` file is created until `buildOneFromSource` is explicitly called. The `isBuildUpToDate` flag accurately tracks whether the built file exists. The duplicate inherits this flag (line 175), so the duplicated logic function correctly reflects that it also needs building.

**Monitoring improvement:** The fix itself eliminates the Sentry noise — by not attempting to copy non-existent built files, the S3 "No objects found" error no longer fires. No separate monitoring changes needed.
2026-03-27 14:47:39 +00:00
2 changed files with 65 additions and 3 deletions
@@ -59,6 +59,16 @@ type CopySourceParams = Identifier & {
toBuiltHandlerPath: string;
};
type CopySourceResourcesParams = Identifier & {
fromSourceHandlerPath: string;
toSourceHandlerPath: string;
};
type CopyBuiltResourcesParams = Identifier & {
fromBuiltHandlerPath: string;
toBuiltHandlerPath: string;
};
@Injectable()
export class LogicFunctionResourceService {
constructor(private readonly fileStorageService: FileStorageService) {}
@@ -248,6 +258,50 @@ export class LogicFunctionResourceService {
});
}
async copySourceResources({
fromSourceHandlerPath,
toSourceHandlerPath,
workspaceId,
applicationUniversalIdentifier,
}: CopySourceResourcesParams): Promise<void> {
await this.fileStorageService.copy({
from: {
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
resourcePath: fromSourceHandlerPath,
},
to: {
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
resourcePath: toSourceHandlerPath,
},
});
}
async copyBuiltResources({
fromBuiltHandlerPath,
toBuiltHandlerPath,
workspaceId,
applicationUniversalIdentifier,
}: CopyBuiltResourcesParams): Promise<void> {
await this.fileStorageService.copy({
from: {
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
resourcePath: fromBuiltHandlerPath,
},
to: {
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
resourcePath: toBuiltHandlerPath,
},
});
}
async copyDependenciesInMemory({
applicationUniversalIdentifier,
workspaceId,
@@ -155,15 +155,23 @@ export class LogicFunctionFromSourceService {
newId,
);
await this.logicFunctionResourceService.copyResources({
await this.logicFunctionResourceService.copySourceResources({
fromSourceHandlerPath: sourceHandlerPath,
toSourceHandlerPath,
fromBuiltHandlerPath: builtHandlerPath,
toBuiltHandlerPath,
workspaceId,
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
});
if (existingLogicFunction.isBuildUpToDate) {
await this.logicFunctionResourceService.copyBuiltResources({
fromBuiltHandlerPath: builtHandlerPath,
toBuiltHandlerPath,
workspaceId,
applicationUniversalIdentifier:
ownerFlatApplication.universalIdentifier,
});
}
const universalFlatLogicFunctionToCreate =
buildUniversalFlatLogicFunctionToCreate({
id: newId,