From 2bff457506c79696dae1e549b52f43b9bf38938f Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:10:07 +0100 Subject: [PATCH 01/13] Remove unused store map getters from summary --- .../WorkflowInvocationSummary.test.js | 21 ------------------- .../WorkflowInvocationSummary.vue | 2 -- 2 files changed, 23 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.test.js b/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.test.js index a5e95500ef40..e98d77fbc96b 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.test.js +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.test.js @@ -1,20 +1,9 @@ import { shallowMount } from "@vue/test-utils"; import { getLocalVue } from "tests/jest/helpers"; -import Vuex from "vuex"; import invocationData from "../Workflow/test/json/invocation.json"; import WorkflowInvocationSummary from "./WorkflowInvocationSummary"; -const invocationJobsSummaryById = { - id: "d9833097445452b0", - model: "WorkflowInvocation", - states: {}, - populated_state: "ok", -}; - -const mockComputed = { - getInvocationJobsSummaryById: () => () => invocationJobsSummaryById, -}; const localVue = getLocalVue(); @@ -30,7 +19,6 @@ describe("WorkflowInvocationSummary.vue with terminal invocation", () => { }; wrapper = shallowMount(WorkflowInvocationSummary, { propsData, - computed: mockComputed, localVue, }); }); @@ -49,16 +37,8 @@ describe("WorkflowInvocationSummary.vue with invocation scheduling running", () let wrapper; let propsData; let store; - let actions; beforeEach(async () => { - actions = { - fetchInvocationForId: jest.fn(), - fetchInvocationJobsSummaryForId: jest.fn(), - }; - store = new Vuex.Store({ - actions, - }); propsData = { invocation: invocationData, invocationAndJobTerminal: false, @@ -67,7 +47,6 @@ describe("WorkflowInvocationSummary.vue with invocation scheduling running", () wrapper = shallowMount(WorkflowInvocationSummary, { store, propsData, - computed: mockComputed, localVue, }); }); diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.vue index 3b30faf87d73..4b90ce1b5f2b 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationSummary.vue @@ -68,7 +68,6 @@ import mixin from "components/JobStates/mixin"; import LoadingSpan from "components/LoadingSpan"; import ProgressBar from "components/ProgressBar"; import { getRootFromIndexLink } from "onload"; -import { mapGetters } from "vuex"; import InvocationMessage from "@/components/WorkflowInvocationState/InvocationMessage.vue"; @@ -112,7 +111,6 @@ export default { }; }, computed: { - ...mapGetters(["getInvocationById", "getInvocationJobsSummaryById"]), invocationId() { return this.invocation?.id; }, From 37cbe3563e9f74810fba15585846cc486d856c66 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:19:09 +0100 Subject: [PATCH 02/13] Remove unused vuex references from step component --- .../WorkflowInvocationState/WorkflowInvocationStep.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue index ea5da90e8b45..d37e6fa1580b 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue @@ -95,7 +95,6 @@ import WorkflowIcons from "components/Workflow/icons"; import { mapActions, mapState } from "pinia"; import { useToolStore } from "stores/toolStore"; import { useWorkflowStore } from "stores/workflowStore"; -import { mapActions as vuexMapActions, mapGetters } from "vuex"; import JobStep from "./JobStep"; import ParameterStep from "./ParameterStep"; @@ -126,7 +125,6 @@ export default { computed: { ...mapState(useWorkflowStore, ["getStoredWorkflowByInstanceId"]), ...mapState(useToolStore, ["getToolForId", "getToolNameById"]), - ...mapGetters(["getInvocationStepById"]), isReady() { return this.invocation.steps.length > 0; }, @@ -156,7 +154,6 @@ export default { methods: { ...mapActions(useWorkflowStore, ["fetchWorkflowForInstanceId"]), ...mapActions(useToolStore, ["fetchToolForId"]), - ...vuexMapActions(["fetchInvocationStepById"]), fetchTool() { if (this.workflowStep.tool_id && !this.getToolForId(this.workflowStep.tool_id)) { this.fetchToolForId(this.workflowStep.tool_id); From 0894623ffe444470b396a7daaddedb62438fcd62 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:01:52 +0100 Subject: [PATCH 03/13] Add provisional invocation API types and fetchers The interfaces should be replaced by the generated schema models and the fetch functions with the generated schema fetchers. --- client/src/api/invocations.ts | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 client/src/api/invocations.ts diff --git a/client/src/api/invocations.ts b/client/src/api/invocations.ts new file mode 100644 index 000000000000..75cf94a1455a --- /dev/null +++ b/client/src/api/invocations.ts @@ -0,0 +1,42 @@ +import axios from "axios"; + +import { getAppRoot } from "@/onload"; + +import { ApiResponse } from "./schema"; + +// TODO: Replace these interfaces with real schema models after https://github.com/galaxyproject/galaxy/pull/16707 is merged +export interface WorkflowInvocationDetails { + id: string; +} + +export interface WorkflowInvocationJobsSummary { + id: string; +} + +export interface WorkflowInvocationStep { + id: string; +} + +// TODO: Replace these provisional functions with fetchers after https://github.com/galaxyproject/galaxy/pull/16707 is merged +export async function fetchInvocationDetails(params: { id: string }): Promise> { + const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}`); + return { + data, + } as ApiResponse; +} + +export async function fetchInvocationJobsSummary(params: { + id: string; +}): Promise> { + const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}/jobs_summary`); + return { + data, + } as ApiResponse; +} + +export async function fetchInvocationStep(params: { id: string }): Promise> { + const { data } = await axios.get(`${getAppRoot()}api/invocations//any/steps/${params.id}`); + return { + data, + } as ApiResponse; +} From 2ce57b3bc78672522ff97010ad457a573526ed6e Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:03:39 +0100 Subject: [PATCH 04/13] Add Pinia store for workflow invocations --- client/src/stores/invocationStore.ts | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 client/src/stores/invocationStore.ts diff --git a/client/src/stores/invocationStore.ts b/client/src/stores/invocationStore.ts new file mode 100644 index 000000000000..2726781ade2c --- /dev/null +++ b/client/src/stores/invocationStore.ts @@ -0,0 +1,31 @@ +import { defineStore } from "pinia"; + +import { + fetchInvocationDetails, + fetchInvocationJobsSummary, + fetchInvocationStep, + type WorkflowInvocationDetails, + type WorkflowInvocationJobsSummary, + type WorkflowInvocationStep, +} from "@/api/invocations"; +import { useKeyedCache } from "@/composables/keyedCache"; + +export const useInvocationStore = defineStore("invocationStore", () => { + const { getItemById: getInvocationById, fetchItemById: fetchInvocationForId } = + useKeyedCache(fetchInvocationDetails); + + const { getItemById: getInvocationJobsSummaryById, fetchItemById: fetchInvocationJobsSummaryForId } = + useKeyedCache(fetchInvocationJobsSummary); + + const { getItemById: getInvocationStepById, fetchItemById: fetchInvocationStepById } = + useKeyedCache(fetchInvocationStep); + + return { + getInvocationById, + fetchInvocationForId, + getInvocationJobsSummaryById, + fetchInvocationJobsSummaryForId, + getInvocationStepById, + fetchInvocationStepById, + }; +}); From 989028dcd263095c752424829210efdb0be2259a Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:04:20 +0100 Subject: [PATCH 05/13] Refactor WorkflowInvocationState.vue to use invocationStore --- .../WorkflowInvocationState.vue | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index bb26b0769cd8..4da47b48b157 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -34,7 +34,8 @@ import mixin from "components/JobStates/mixin"; import LoadingSpan from "components/LoadingSpan"; import JOB_STATES_MODEL from "utils/job-states-model"; -import { mapActions, mapGetters } from "vuex"; + +import { useInvocationStore } from "@/stores/invocationStore"; import { cancelWorkflowScheduling } from "./services"; @@ -61,6 +62,12 @@ export default { default: null, }, }, + setup() { + const invocationStore = useInvocationStore(); + return { + invocationStore, + }; + }, data() { return { stepStatesInterval: null, @@ -68,9 +75,8 @@ export default { }; }, computed: { - ...mapGetters(["getInvocationById", "getInvocationJobsSummaryById"]), invocation: function () { - return this.getInvocationById(this.invocationId); + return this.invocationStore.getInvocationById(this.invocationId); }, invocationState: function () { return this.invocation?.state || "new"; @@ -93,7 +99,7 @@ export default { return this.jobStatesSummary && this.jobStatesSummary.terminal(); }, jobStatesSummary() { - const jobsSummary = this.getInvocationJobsSummaryById(this.invocationId); + const jobsSummary = this.invocationStore.getInvocationJobsSummaryById(this.invocationId); return !jobsSummary ? null : new JOB_STATES_MODEL.JobStatesSummary(jobsSummary); }, }, @@ -106,17 +112,16 @@ export default { clearTimeout(this.stepStatesInterval); }, methods: { - ...mapActions(["fetchInvocationForId", "fetchInvocationJobsSummaryForId"]), pollStepStatesUntilTerminal: function () { if (!this.invocation || !this.invocationSchedulingTerminal) { - this.fetchInvocationForId(this.invocationId).then((response) => { + this.invocationStore.fetchInvocationForId({ id: this.invocationId }).then((response) => { this.stepStatesInterval = setTimeout(this.pollStepStatesUntilTerminal, 3000); }); } }, pollJobStatesUntilTerminal: function () { if (!this.jobStatesTerminal) { - this.fetchInvocationJobsSummaryForId(this.invocationId).then((response) => { + this.invocationStore.fetchInvocationJobsSummaryForId({ id: this.invocationId }).then((response) => { this.jobStatesInterval = setTimeout(this.pollJobStatesUntilTerminal, 3000); }); } From cf2cc5d0c6a98fecee3975a63b231a28f2be2754 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Fri, 15 Dec 2023 13:05:08 +0100 Subject: [PATCH 06/13] Remove unused Vuex invocationStore from store configuration --- client/src/store/index.js | 2 -- client/src/store/invocationStore.js | 55 ----------------------------- 2 files changed, 57 deletions(-) delete mode 100644 client/src/store/invocationStore.js diff --git a/client/src/store/index.js b/client/src/store/index.js index 050cc5a929c0..0c7efe1a28e6 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -10,7 +10,6 @@ import createCache from "vuex-cache"; import VuexPersistence from "vuex-persist"; import { gridSearchStore } from "./gridSearchStore"; -import { invocationStore } from "./invocationStore"; import { syncVuextoGalaxy } from "./syncVuextoGalaxy"; import { tagStore } from "./tagStore"; @@ -39,7 +38,6 @@ export function createStore() { const storeConfig = { plugins: [createCache(), panelsPersistence.plugin], modules: { - invocations: invocationStore, gridSearch: gridSearchStore, tags: tagStore, }, diff --git a/client/src/store/invocationStore.js b/client/src/store/invocationStore.js deleted file mode 100644 index 348b43aa3e96..000000000000 --- a/client/src/store/invocationStore.js +++ /dev/null @@ -1,55 +0,0 @@ -import axios from "axios"; -import { getAppRoot } from "onload/loadConfig"; -import Vue from "vue"; - -export const state = { - invocationDetailsById: {}, - invocationJobsSummaryById: {}, - invocationStepById: {}, -}; - -const getters = { - getInvocationById: (state) => (invocationId) => { - return state.invocationDetailsById[invocationId]; - }, - getInvocationJobsSummaryById: (state) => (invocationId) => { - return state.invocationJobsSummaryById[invocationId]; - }, - getInvocationStepById: (state) => (invocationStepId) => { - return state.invocationStepById[invocationStepId]; - }, -}; - -const actions = { - fetchInvocationForId: async ({ commit }, invocationId) => { - const { data } = await axios.get(`${getAppRoot()}api/invocations/${invocationId}`); - commit("saveInvocationForId", { invocationId, invocationData: data }); - }, - fetchInvocationJobsSummaryForId: async ({ commit }, invocationId) => { - const { data } = await axios.get(`${getAppRoot()}api/invocations/${invocationId}/jobs_summary`); - commit("saveInvocationJobsSummaryForId", { invocationId, jobsSummary: data }); - }, - fetchInvocationStepById: async ({ commit }, invocationStepId) => { - const { data } = await axios.get(`${getAppRoot()}api/invocations/any/steps/${invocationStepId}`); - commit("saveInvocationStepById", { invocationStepId, invocationStepData: data }); - }, -}; - -const mutations = { - saveInvocationForId: (state, { invocationId, invocationData }) => { - Vue.set(state.invocationDetailsById, invocationId, invocationData); - }, - saveInvocationJobsSummaryForId: (state, { invocationId, jobsSummary }) => { - Vue.set(state.invocationJobsSummaryById, invocationId, jobsSummary); - }, - saveInvocationStepById: (state, { invocationStepId, invocationStepData }) => { - Vue.set(state.invocationStepById, invocationStepId, invocationStepData); - }, -}; - -export const invocationStore = { - state, - getters, - actions, - mutations, -}; From 64563b3914d307f75ea78988494222f8d599d290 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:57:10 +0100 Subject: [PATCH 07/13] Use async/await when polling invocation state --- .../WorkflowInvocationState.vue | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index 4da47b48b157..0182824417d2 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -112,18 +112,16 @@ export default { clearTimeout(this.stepStatesInterval); }, methods: { - pollStepStatesUntilTerminal: function () { + pollStepStatesUntilTerminal: async function () { if (!this.invocation || !this.invocationSchedulingTerminal) { - this.invocationStore.fetchInvocationForId({ id: this.invocationId }).then((response) => { - this.stepStatesInterval = setTimeout(this.pollStepStatesUntilTerminal, 3000); - }); + await this.invocationStore.fetchInvocationForId({ id: this.invocationId }); + this.stepStatesInterval = setTimeout(this.pollStepStatesUntilTerminal, 3000); } }, - pollJobStatesUntilTerminal: function () { + pollJobStatesUntilTerminal: async function () { if (!this.jobStatesTerminal) { - this.invocationStore.fetchInvocationJobsSummaryForId({ id: this.invocationId }).then((response) => { - this.jobStatesInterval = setTimeout(this.pollJobStatesUntilTerminal, 3000); - }); + await this.invocationStore.fetchInvocationJobsSummaryForId({ id: this.invocationId }); + this.jobStatesInterval = setTimeout(this.pollJobStatesUntilTerminal, 3000); } }, onError: function (e) { From 98f99c0a227e6e7c1548468f0a2c824349355bb1 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:59:28 +0100 Subject: [PATCH 08/13] Remove unused prop argument in WorkflowInvocationDetails --- .../WorkflowInvocationState/WorkflowInvocationState.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index 0182824417d2..1d7adff5e90f 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -10,9 +10,7 @@ @invocation-cancelled="cancelWorkflowScheduling" /> - +