Skip to content

Commit

Permalink
[24.1] Allow running and editing workflows for specific versions
Browse files Browse the repository at this point in the history
This passes the version prop to the routes for editing and running workflows also and to the `InvokeWorkflowPayload`, so that users can run specific versions of workflows.

Fixes galaxyproject#18367
  • Loading branch information
ahmedhamidawan committed Jun 11, 2024
1 parent b8e2bfc commit e3ee4a9
Show file tree
Hide file tree
Showing 14 changed files with 64 additions and 16 deletions.
5 changes: 5 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8555,6 +8555,11 @@ export interface components {
* @default false
*/
use_cached_job?: boolean | null;
/**
* Version
* @description The version of the workflow to invoke.
*/
version?: number | null;
};
/**
* ItemTagsCreatePayload
Expand Down
4 changes: 3 additions & 1 deletion client/src/components/Workflow/Editor/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,9 @@ export default {
this.report.markdown = markdown;
},
onRun() {
const runUrl = `/workflows/run?id=${this.id}`;
const runUrl = `/workflows/run?id=${this.id}${
this.version !== undefined ? `&version=${this.version}` : ""
}`;
this.onNavigate(runUrl);
},
async onNavigate(url, forceSave = false, ignoreChanges = false) {
Expand Down
10 changes: 7 additions & 3 deletions client/src/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ const router = useRouter();
interface Props {
workflowId: string;
version?: string;
preferSimpleForm?: boolean;
simpleFormTargetHistory?: string;
simpleFormUseJobCache?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
version: undefined,
preferSimpleForm: false,
simpleFormTargetHistory: "current",
simpleFormUseJobCache: false,
Expand All @@ -49,7 +51,9 @@ const workflowName = ref("");
const workflowModel: any = ref(null);
const currentHistoryId = computed(() => historyStore.currentHistoryId);
const editorLink = computed(() => `/workflows/edit?id=${props.workflowId}`);
const editorLink = computed(
() => `/workflows/edit?id=${props.workflowId}${props.version ? `&version=${props.version}` : ""}`
);
const historyStatusKey = computed(() => `${currentHistoryId.value}_${lastUpdateTime.value}`);
const isOwner = computed(() => currentUser.value?.username === workflowModel.value.runData.owner);
const lastUpdateTime = computed(() => historyItemsStore.lastUpdateTime);
Expand All @@ -74,7 +78,7 @@ function handleSubmissionError(error: string) {
}
function loadRun() {
getRunData(props.workflowId)
getRunData(props.workflowId, props.version || undefined)
.then((runData) => {
const incomingModel = new WorkflowRunModel(runData);
simpleForm.value = props.preferSimpleForm;
Expand Down Expand Up @@ -116,7 +120,7 @@ function loadRun() {
}
async function onImport() {
const response = await copyWorkflow(props.workflowId, workflowModel.value.runData.owner);
const response = await copyWorkflow(props.workflowId, workflowModel.value.runData.owner, props.version);
router.push(`/workflows/edit?id=${response.id}`);
}
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</span>
</BAlert>
<div class="h4 clearfix mb-3">
<b>Workflow: {{ model.name }}</b>
<b>Workflow: {{ model.name }}</b> <i>(version: {{ model.runData.version + 1 }})</i>
<ButtonSpinner
id="run-workflow"
class="float-right"
Expand Down Expand Up @@ -231,6 +231,7 @@ export default {
// the user is already warned if tool versions are wrong,
// they can still choose to invoke the workflow anyway.
require_exact_tool_versions: false,
version: this.model.runData.version,
};
console.debug("WorkflowRunForm::onExecute()", "Ready for submission.", jobDef);
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
or send the results to a new one using the run settings ⚙️
</span>
</BAlert>
<b>Workflow: {{ model.name }}</b>
<b>Workflow: {{ model.name }}</b> <i>(version: {{ model.runData.version + 1 }})</i>
<ButtonSpinner
id="run-workflow"
:wait="waitingForRequest"
Expand Down Expand Up @@ -200,6 +200,7 @@ export default {
batch: true,
use_cached_job: this.useCachedJobs,
require_exact_tool_versions: false,
version: this.model.runData.version,
};
if (this.sendToNewHistory) {
data.new_history_name = this.model.name;
Expand Down
8 changes: 6 additions & 2 deletions client/src/components/Workflow/Run/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import { rethrowSimple } from "utils/simple-error";
* for implementation). This contains the data needed to render the UI for workflows.
*
* @param {String} workflowId - (Stored?) Workflow ID to fetch data for.
* @param {String} version - Version of the workflow to fetch.
*/
export async function getRunData(workflowId) {
const url = `${getAppRoot()}api/workflows/${workflowId}/download?style=run`;
export async function getRunData(workflowId, version = null) {
let url = `${getAppRoot()}api/workflows/${workflowId}/download?style=run`;
if (version) {
url += `&version=${version}`;
}
try {
const response = await axios.get(url);
return response.data;
Expand Down
10 changes: 8 additions & 2 deletions client/src/components/Workflow/WorkflowRunButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { library } from "@fortawesome/fontawesome-svg-core";
import { faPlay } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { computed } from "vue";
library.add(faPlay);
Expand All @@ -11,9 +12,14 @@ interface Props {
full?: boolean;
title?: string;
disabled?: boolean;
version?: number;
}
defineProps<Props>();
const props = defineProps<Props>();
const runPath = computed(
() => `/workflows/run?id=${props.id}${props.version !== undefined ? `&version=${props.version}` : ""}`
);
</script>

<template>
Expand All @@ -25,7 +31,7 @@ defineProps<Props>();
variant="primary"
size="sm"
:disabled="disabled"
:to="`/workflows/run?id=${id}`">
:to="runPath">
<FontAwesomeIcon :icon="faPlay" fixed-width />

<span v-if="full" v-localize>Run</span>
Expand Down
8 changes: 6 additions & 2 deletions client/src/components/Workflow/workflows.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ export async function updateWorkflow(id: string, changes: object): Promise<Workf
return data;
}

export async function copyWorkflow(id: string, currentOwner: string): Promise<Workflow> {
const { data: workflowData } = await axios.get(withPrefix(`/api/workflows/${id}/download`));
export async function copyWorkflow(id: string, currentOwner: string, version?: string): Promise<Workflow> {
let path = `/api/workflows/${id}/download`;
if (version) {
path += `?version=${version}`;
}
const { data: workflowData } = await axios.get(withPrefix(path));

workflowData.name = `Copy of ${workflowData.name}`;
const currentUsername = useUserStore().currentUser?.username;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function getWorkflowName() {
size="sm"
variant="secondary"
:disabled="isDeletedWorkflow"
:to="`/workflows/edit?id=${getWorkflowId()}`">
:to="`/workflows/edit?id=${getWorkflowId()}&version=${workflowVersion}`">
<FontAwesomeIcon :icon="faEdit" />
<span v-localize>Edit</span>
</BButton>
Expand All @@ -218,7 +218,8 @@ function getWorkflowName() {
: 'This workflow has been deleted.'
"
:disabled="isDeletedWorkflow"
full />
full
:version="workflowVersion" />
</BButtonGroup>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions client/src/entry/analysis/modules/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default {
},
workflowParams() {
const workflowId = this.query.workflow_id;
const version = this.query.version;
let preferSimpleForm = this.config.simplified_workflow_run_ui == "prefer";
const preferSimpleFormOverride = this.query.simplified_workflow_run_ui;
if (preferSimpleFormOverride == "prefer") {
Expand All @@ -68,6 +69,7 @@ export default {
const simpleFormUseJobCache = this.config.simplified_workflow_run_ui_job_cache == "on";
return {
workflowId,
version,
preferSimpleForm,
simpleFormTargetHistory,
simpleFormUseJobCache,
Expand Down
5 changes: 5 additions & 0 deletions client/src/entry/analysis/modules/WorkflowEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default {
return {
storedWorkflowId: null,
workflowId: null,
version: null,
editorConfig: null,
editorReloadKey: 0,
};
Expand All @@ -46,6 +47,7 @@ export default {
this.storedWorkflowId = Query.get("id");
this.workflowId = Query.get("workflow_id");
this.version = Query.get("version");
const params = {};
Expand All @@ -54,6 +56,9 @@ export default {
} else if (this.storedWorkflowId) {
params.id = this.storedWorkflowId;
}
if (this.version) {
params.version = this.version;
}
this.editorConfig = await urlData({ url: "/workflow/editor", params });
Expand Down
5 changes: 4 additions & 1 deletion client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,10 @@ export function getRouter(Galaxy) {
redirect: redirectAnon(),
props: (route) => ({
config: Galaxy.config,
query: { workflow_id: route.query.id },
query: {
workflow_id: route.query.id,
version: route.query.version,
},
}),
},
{
Expand Down
5 changes: 5 additions & 0 deletions lib/galaxy/schema/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class GetTargetHistoryPayload(Model):

class InvokeWorkflowPayload(GetTargetHistoryPayload):
# TODO - Are the descriptions correct?
version: Optional[int] = Field(
None,
title="Version",
description="The version of the workflow to invoke.",
)
instance: Optional[bool] = Field(
False,
title="Is instance",
Expand Down
7 changes: 6 additions & 1 deletion lib/galaxy/webapps/galaxy/services/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ def invoke_workflow(
# Get workflow + accessibility check.
by_stored_id = not payload.instance
stored_workflow = self._workflows_manager.get_stored_accessible_workflow(trans, workflow_id, by_stored_id)
workflow = stored_workflow.latest_workflow
version = payload.version
if version == "":
version = None
if version is not None:
version = int(version)
workflow = stored_workflow.get_internal_version(version)
run_configs = build_workflow_run_configs(trans, workflow, payload.model_dump(exclude_unset=True))
is_batch = payload.batch
if not is_batch and len(run_configs) != 1:
Expand Down

0 comments on commit e3ee4a9

Please sign in to comment.