Skip to content

Commit 4df25fa

Browse files
committed
Use parameter models within Galaxy.
1 parent c5bbfe1 commit 4df25fa

File tree

33 files changed

+2520
-190
lines changed

33 files changed

+2520
-190
lines changed

.github/workflows/framework_tools.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
strategy:
2727
matrix:
2828
python-version: ['3.8']
29+
use-legacy-api: ['0', '1']
2930
services:
3031
postgres:
3132
image: postgres:13
@@ -66,7 +67,7 @@ jobs:
6667
path: 'galaxy root/.venv'
6768
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-framework-tools
6869
- name: Run tests
69-
run: ./run_tests.sh --coverage --framework-tools
70+
run: GALAXY_TEST_USE_LEGACY_TOOL_API="${{ matrix.use-legacy-api }}" ./run_tests.sh --coverage --framework-tools
7071
working-directory: 'galaxy root'
7172
- uses: codecov/codecov-action@v3
7273
with:

client/src/components/Tool/ToolForm.vue

Lines changed: 160 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
<template v-slot:header-buttons>
9090
<ButtonSpinner
9191
id="execute"
92-
title="Run Tool"
92+
:title="runButtonTitle"
9393
:disabled="!canMutateHistory"
9494
class="btn-sm"
9595
:wait="showExecuting"
@@ -98,7 +98,7 @@
9898
</template>
9999
<template v-slot:buttons>
100100
<ButtonSpinner
101-
title="Run Tool"
101+
:title="runButtonTitle"
102102
class="mt-3 mb-3"
103103
:disabled="!canMutateHistory"
104104
:wait="showExecuting"
@@ -111,12 +111,14 @@
111111

112112
<script>
113113
import { getGalaxyInstance } from "app";
114+
import axios from "axios";
114115
import ButtonSpinner from "components/Common/ButtonSpinner";
115116
import Heading from "components/Common/Heading";
116117
import FormDisplay from "components/Form/FormDisplay";
117118
import FormElement from "components/Form/FormElement";
118119
import LoadingSpan from "components/LoadingSpan";
119120
import ToolEntryPoints from "components/ToolEntryPoints/ToolEntryPoints";
121+
import { getAppRoot } from "onload/loadConfig";
120122
import { mapActions, mapState, storeToRefs } from "pinia";
121123
import { useHistoryItemsStore } from "stores/historyItemsStore";
122124
import { useJobStore } from "stores/jobStore";
@@ -128,7 +130,8 @@ import { useHistoryStore } from "@/stores/historyStore";
128130
import { useUserStore } from "@/stores/userStore";
129131
130132
import ToolRecommendation from "../ToolRecommendation";
131-
import { getToolFormData, submitJob, updateToolFormData } from "./services";
133+
import { getToolFormData, getToolInputs, submitJob, submitToolRequest, updateToolFormData } from "./services";
134+
import { structuredInputs } from "./structured";
132135
import ToolCard from "./ToolCard";
133136
import { allowCachedJobs } from "./utilities";
134137
@@ -204,6 +207,8 @@ export default {
204207
],
205208
immutableHistoryMessage:
206209
"This history is immutable and you cannot run tools in it. Please switch to a different history.",
210+
toolInputs: null,
211+
submissionStateMessage: null,
207212
};
208213
},
209214
computed: {
@@ -249,7 +254,15 @@ export default {
249254
return this.currentHistory && canMutateHistory(this.currentHistory);
250255
},
251256
runButtonTitle() {
252-
return "Run Tool";
257+
if (this.showExecuting) {
258+
if (this.submissionStateMessage) {
259+
return this.submissionStateMessage;
260+
} else {
261+
return "Run Tool";
262+
}
263+
} else {
264+
return "Run Tool";
265+
}
253266
},
254267
},
255268
watch: {
@@ -301,11 +314,38 @@ export default {
301314
onChangeVersion(newVersion) {
302315
this.requestTool(newVersion);
303316
},
317+
waitOnRequest(response, requestContent, config, prevRoute) {
318+
const toolRequestId = response.tool_request_id;
319+
const handleRequestState = (toolRequestStateResponse) => {
320+
const state = toolRequestStateResponse.data;
321+
console.log(`state is ${state}`);
322+
if (["new"].indexOf(state) !== -1) {
323+
setTimeout(doRequestCheck, 1000);
324+
} else if (state == "failed") {
325+
this.handleError(null, requestContent);
326+
} else {
327+
refreshContentsWrapper();
328+
this.showForm = false;
329+
this.showSuccess = true;
330+
this.handleSubmissionComplete(config, prevRoute);
331+
}
332+
};
333+
const doRequestCheck = () => {
334+
axios
335+
.get(`${getAppRoot()}api/tool_requests/${toolRequestId}/state`)
336+
.then(handleRequestState)
337+
.catch((e) => this.handleError(e, requestContent));
338+
};
339+
setTimeout(doRequestCheck, 1000);
340+
},
304341
requestTool(newVersion) {
305342
this.currentVersion = newVersion || this.currentVersion;
306343
this.disabled = true;
307344
this.loading = true;
308345
console.debug("ToolForm - Requesting tool.", this.id);
346+
getToolInputs(this.id, this.currentVersion).then((data) => {
347+
this.toolInputs = data;
348+
});
309349
return getToolFormData(this.id, this.currentVersion, this.job_id, this.history_id)
310350
.then((data) => {
311351
this.formConfig = data;
@@ -331,90 +371,135 @@ export default {
331371
onUpdatePreferredObjectStoreId(preferredObjectStoreId) {
332372
this.preferredObjectStoreId = preferredObjectStoreId;
333373
},
374+
handleSubmissionComplete(config, prevRoute) {
375+
const changeRoute = prevRoute === this.$route.fullPath;
376+
if (changeRoute) {
377+
this.$router.push(`/jobs/submission/success`);
378+
} else {
379+
if ([true, "true"].includes(config.enable_tool_recommendations)) {
380+
this.showRecommendation = true;
381+
}
382+
document.querySelector(".center-panel").scrollTop = 0;
383+
}
384+
},
385+
handleError(e, errorContent) {
386+
this.errorMessage = e?.response?.data?.err_msg;
387+
this.showExecuting = false;
388+
this.submissionStateMessage = null;
389+
let genericError = true;
390+
const errorData = e && e.response && e.response.data && e.response.data.err_data;
391+
if (errorData) {
392+
const errorEntries = Object.entries(errorData);
393+
if (errorEntries.length > 0) {
394+
this.validationScrollTo = errorEntries[0];
395+
genericError = false;
396+
}
397+
}
398+
if (genericError) {
399+
this.showError = true;
400+
this.errorTitle = "Job submission failed.";
401+
this.errorContent = errorContent;
402+
}
403+
},
334404
onExecute(config, historyId) {
335405
if (this.validationInternal) {
336406
this.validationScrollTo = this.validationInternal.slice();
337407
return;
338408
}
339409
this.showExecuting = true;
340-
const jobDef = {
341-
history_id: historyId,
342-
tool_id: this.formConfig.id,
343-
tool_version: this.formConfig.version,
344-
inputs: {
345-
...this.formData,
346-
},
410+
this.submissionStateMessage = "Preparing Request";
411+
const inputs = {
412+
...this.formData,
347413
};
348-
if (this.useEmail) {
349-
jobDef.inputs["send_email_notification"] = true;
350-
}
351-
if (this.useJobRemapping) {
352-
jobDef.inputs["rerun_remap_job_id"] = this.job_id;
353-
}
354-
if (this.useCachedJobs) {
355-
jobDef.inputs["use_cached_job"] = true;
414+
const toolId = this.formConfig.id;
415+
const toolVersion = this.formConfig.version;
416+
let validatedInputs = null;
417+
try {
418+
validatedInputs = structuredInputs(inputs, this.toolInputs);
419+
} catch {
420+
// failed validation, just use legacy API
356421
}
357-
if (this.preferredObjectStoreId) {
358-
jobDef.preferred_object_store_id = this.preferredObjectStoreId;
359-
}
360-
if (this.dataManagerMode === "bundle") {
361-
jobDef.data_manager_mode = this.dataManagerMode;
362-
}
363-
console.debug("toolForm::onExecute()", jobDef);
364422
const prevRoute = this.$route.fullPath;
365-
submitJob(jobDef).then(
366-
(jobResponse) => {
367-
this.showExecuting = false;
368-
let changeRoute = false;
369-
refreshContentsWrapper();
370-
if (jobResponse.produces_entry_points) {
371-
this.showEntryPoints = true;
372-
this.entryPoints = jobResponse.jobs;
373-
}
374-
const nJobs = jobResponse && jobResponse.jobs ? jobResponse.jobs.length : 0;
375-
if (nJobs > 0) {
376-
this.showForm = false;
377-
const toolName = this.toolName;
378-
this.saveLatestResponse({
379-
jobDef,
380-
jobResponse,
381-
toolName,
382-
});
383-
changeRoute = prevRoute === this.$route.fullPath;
384-
} else {
385-
this.showError = true;
386-
this.showForm = true;
387-
this.errorTitle = "Job submission rejected.";
388-
this.errorContent = jobResponse;
423+
if (validatedInputs) {
424+
const toolRequest = {
425+
history_id: historyId,
426+
tool_id: toolId,
427+
tool_version: toolVersion,
428+
inputs: validatedInputs,
429+
};
430+
if (this.useCachedJobs) {
431+
toolRequest.use_cached_jobs = true;
432+
}
433+
if (this.preferredObjectStoreId) {
434+
toolRequest.preferred_object_store_id = this.preferredObjectStoreId;
435+
}
436+
if (this.dataManagerMode === "bundle") {
437+
toolRequest.data_manager_mode = this.dataManagerMode;
438+
}
439+
this.submissionStateMessage = "Sending Request";
440+
submitToolRequest(toolRequest).then(
441+
(jobResponse) => {
442+
this.submissionStateMessage = "Processing Request";
443+
console.log(jobResponse);
444+
this.waitOnRequest(jobResponse, toolRequest, config, prevRoute);
445+
},
446+
(e) => {
447+
this.handleError(e, toolRequest);
389448
}
390-
if (changeRoute) {
391-
this.$router.push(`/jobs/submission/success`);
392-
} else {
393-
if ([true, "true"].includes(config.enable_tool_recommendations)) {
394-
this.showRecommendation = true;
449+
);
450+
} else {
451+
const jobDef = {
452+
history_id: historyId,
453+
tool_id: toolId,
454+
tool_version: toolVersion,
455+
inputs: inputs,
456+
};
457+
if (this.useEmail) {
458+
jobDef.inputs["send_email_notification"] = true;
459+
}
460+
if (this.useJobRemapping) {
461+
jobDef.inputs["rerun_remap_job_id"] = this.job_id;
462+
}
463+
if (this.useCachedJobs) {
464+
jobDef.inputs["use_cached_job"] = true;
465+
}
466+
if (this.preferredObjectStoreId) {
467+
jobDef.preferred_object_store_id = this.preferredObjectStoreId;
468+
}
469+
if (this.dataManagerMode === "bundle") {
470+
jobDef.data_manager_mode = this.dataManagerMode;
471+
}
472+
console.debug("toolForm::onExecute()", jobDef);
473+
submitJob(jobDef).then(
474+
(jobResponse) => {
475+
this.showExecuting = false;
476+
refreshContentsWrapper();
477+
if (jobResponse.produces_entry_points) {
478+
this.showEntryPoints = true;
479+
this.entryPoints = jobResponse.jobs;
395480
}
396-
document.querySelector("#center").scrollTop = 0;
397-
}
398-
},
399-
(e) => {
400-
this.errorMessage = e?.response?.data?.err_msg;
401-
this.showExecuting = false;
402-
let genericError = true;
403-
const errorData = e && e.response && e.response.data && e.response.data.err_data;
404-
if (errorData) {
405-
const errorEntries = Object.entries(errorData);
406-
if (errorEntries.length > 0) {
407-
this.validationScrollTo = errorEntries[0];
408-
genericError = false;
481+
const nJobs = jobResponse && jobResponse.jobs ? jobResponse.jobs.length : 0;
482+
if (nJobs > 0) {
483+
this.showForm = false;
484+
const toolName = this.toolName;
485+
this.saveLatestResponse({
486+
jobDef,
487+
jobResponse,
488+
toolName,
489+
});
490+
} else {
491+
this.showError = true;
492+
this.showForm = true;
493+
this.errorTitle = "Job submission rejected.";
494+
this.errorContent = jobResponse;
409495
}
496+
this.handleSubmissionComplete(config, prevRoute);
497+
},
498+
(e) => {
499+
this.handleError(e, jobDef);
410500
}
411-
if (genericError) {
412-
this.showError = true;
413-
this.errorTitle = "Job submission failed.";
414-
this.errorContent = jobDef;
415-
}
416-
}
417-
);
501+
);
502+
}
418503
},
419504
},
420505
};

client/src/components/Tool/ToolSuccess.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const jobStore = useJobStore();
1717
const router = useRouter();
1818
1919
const jobDef = computed(() => responseVal.value.jobDef);
20+
const usedToolRequest = computed(() => responseVal.value.usedToolRequest);
2021
const jobResponse = computed(() => responseVal.value.jobResponse);
2122
const responseVal = computed(() => jobStore.getLatestResponse);
2223
const showRecommendation = computed(() => config.value.enable_tool_recommendations);
@@ -37,6 +38,7 @@ if (Object.keys(responseVal.value).length === 0) {
3738
<div v-if="jobResponse?.produces_entry_points">
3839
<ToolEntryPoints v-for="job in jobResponse.jobs" :key="job.id" :job-id="job.id" />
3940
</div>
41+
<ToolSuccessMessage :job-response="jobResponse" :tool-name="toolName" :used-tool-request="usedToolRequest" />
4042
<ToolSuccessMessage :job-response="jobResponse" :tool-name="toolName" />
4143
<Webhook type="tool" :tool-id="jobDef.tool_id" />
4244
<ToolRecommendation v-if="showRecommendation" :tool-id="jobDef.tool_id" />

client/src/components/Tool/ToolSuccessMessage.vue

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
<template>
22
<div class="donemessagelarge">
3-
<p>
4-
Started tool <b>{{ toolName }}</b> and successfully added {{ nJobsText }} to the queue.
5-
</p>
6-
<p>It produces {{ nOutputsText }}:</p>
7-
<ul>
8-
<li v-for="item of jobResponse.outputs" :key="item.hid">
9-
<b>{{ item.hid }}: {{ item.name }}</b>
10-
</li>
11-
</ul>
3+
<div v-if="usedToolRequest">
4+
You used the fancy new API... something new will be here.
5+
<img
6+
src="https://www.animatedimages.org/data/media/695/animated-under-construction-image-0055.gif"
7+
alt="90s style under construction" />
8+
</div>
9+
<div v-else>
10+
<p>
11+
Started tool <b>{{ toolName }}</b> and successfully added {{ nJobsText }} to the queue.
12+
</p>
13+
<p>The tool uses {{ nInputsText }}:</p>
14+
<ul>
15+
<li v-for="item of inputs" :key="item.hid">
16+
<b>{{ item.hid }}: {{ item.name }}</b>
17+
</li>
18+
</ul>
19+
<p>It produces {{ nOutputsText }}:</p>
20+
<ul>
21+
<li v-for="item of jobResponse.outputs" :key="item.hid">
22+
<b>{{ item.hid }}: {{ item.name }}</b>
23+
</li>
24+
</ul>
25+
</div>
1226
<p>
1327
You can check the status of queued jobs and view the resulting data by refreshing the History panel. When
1428
the job has been run the status will change from 'running' to 'finished' if completed successfully or
@@ -28,6 +42,10 @@ export default {
2842
type: String,
2943
required: true,
3044
},
45+
usedToolRequest: {
46+
type: Boolean,
47+
required: true,
48+
},
3149
},
3250
computed: {
3351
nOutputs() {

0 commit comments

Comments
 (0)