From b1b44f9675b06f7735d3aad3545b4e4dcea80c93 Mon Sep 17 00:00:00 2001 From: Maximiliano Osorio Date: Fri, 1 May 2026 18:29:13 -0400 Subject: [PATCH 1/2] fix(tapis): tolerate empty-string parameter bindings in submitExecutions POST /executionEngines/tapis crashed with "Cannot read properties of undefined (reading 'match')" when an input parameter had no default value and the user-supplied binding was an empty string. The empty string is falsy, so neither branch in getInputsParameters assigned parameters[ip.id], and the subsequent regex check on the value threw. The crash propagated into handleSubmissionFailure, which then crashed on this.seeds.length because seeds was never initialized before seedExecutions threw. The cleanup mutation never ran and the original error was masked. - Guard the geojson regex with optional chaining so undefined values pass through unchanged. - Initialize this.seeds = [] at the start of submitExecutions so the failure cleanup path always has a valid array. - Add unit tests covering empty-string, populated, and default-value parameter cases. --- .../tapis/adapters/TapisExecutionService.ts | 1 + src/classes/tapis/helpers.ts | 2 +- src/classes/tapis/tests/helpers.test.ts | 34 ++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/classes/tapis/adapters/TapisExecutionService.ts b/src/classes/tapis/adapters/TapisExecutionService.ts index 702629a..99c0259 100644 --- a/src/classes/tapis/adapters/TapisExecutionService.ts +++ b/src/classes/tapis/adapters/TapisExecutionService.ts @@ -80,6 +80,7 @@ export class TapisExecutionService implements IExecutionService { threadId: string, threadModelId: string ): Promise { + this.seeds = []; try { const app = await this.loadTapisApp(component); console.log("Submitting executions", executions); diff --git a/src/classes/tapis/helpers.ts b/src/classes/tapis/helpers.ts index c7fb72c..6a5ce61 100644 --- a/src/classes/tapis/helpers.ts +++ b/src/classes/tapis/helpers.ts @@ -109,7 +109,7 @@ export function getInputsParameters(model: Model, bindings: InputBindings, regio parameters[ip.id] = value.toString(); } // HACK: Replace region geojson - if (parameters[ip.id].match(/__region_geojson:(.+)/)) { + if (parameters[ip.id]?.match(/__region_geojson:(.+)/)) { const region_geojson = _getRegionGeoJson(region); parameters[ip.id] = region_geojson; } diff --git a/src/classes/tapis/tests/helpers.test.ts b/src/classes/tapis/tests/helpers.test.ts index 5a701c8..0067f1d 100644 --- a/src/classes/tapis/tests/helpers.test.ts +++ b/src/classes/tapis/tests/helpers.test.ts @@ -1,6 +1,8 @@ import model from "@/classes/tapis/fixtures/model"; import bindings from "@/classes/tapis/fixtures/input-bindings"; -import { getInputDatasets } from "@/classes/tapis/helpers"; +import { getInputDatasets, getInputsParameters } from "@/classes/tapis/helpers"; +import { Model, Region } from "@/classes/mint/mint-types"; + test("get inputs datasets", () => { const key = "https://w3id.org/okn/i/mint/modflow_2005_BartonSprings_avg_Bas"; const datasets = getInputDatasets(model, bindings); @@ -14,3 +16,33 @@ test("get inputs datasets", () => { "https://data.mint.isi.edu/files/sample-inputs-outputs/modflowInputs/BARTON_SPRINGS_2001_2010AVERAGE.ba6" ); }); + +describe("getInputsParameters", () => { + const region = { geometries: [] } as unknown as Region; + const paramId = "https://w3id.org/okn/i/mint/4103a17f-9927-4c76-824d-753daecd0698"; + + const buildModel = (params: unknown[]): Model => + ({ input_parameters: params } as unknown as Model); + + test("does not throw when binding value is empty string and parameter has no default", () => { + const m = buildModel([{ id: paramId, type: "string" }]); + const empty: Record = { [paramId]: "" }; + expect(() => getInputsParameters(m, empty as never, region)).not.toThrow(); + const { parameters } = getInputsParameters(m, empty as never, region); + expect(parameters[paramId]).toBeUndefined(); + }); + + test("uses binding value when provided", () => { + const m = buildModel([{ id: paramId, type: "int" }]); + const b: Record = { [paramId]: "42" }; + const { parameters, parameterTypes } = getInputsParameters(m, b as never, region); + expect(parameters[paramId]).toBe("42"); + expect(parameterTypes[paramId]).toBe("int"); + }); + + test("falls back to ip.value default", () => { + const m = buildModel([{ id: paramId, type: "string", value: "default" }]); + const { parameters } = getInputsParameters(m, {} as never, region); + expect(parameters[paramId]).toBe("default"); + }); +}); From 542063198bd3910e47ac7c7ffe17ac4f7aef8ae6 Mon Sep 17 00:00:00 2001 From: Maximiliano Osorio Date: Fri, 1 May 2026 18:37:13 -0400 Subject: [PATCH 2/2] fix: use node 24 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e30048d..5356ef0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:19-alpine +FROM node:24-alpine # # Install cwltool # RUN apk add --update \