diff --git a/client/package.json b/client/package.json index 0e56c46fbe0..a4a02d84195 100644 --- a/client/package.json +++ b/client/package.json @@ -66,6 +66,7 @@ "markdown-it": "^12.3.2", "markdown-it-regexp": "^0.4.0", "object-hash": "^3.0.0", + "pinia": "^2.0.23", "popper.js": "^1.16.1", "pretty-bytes": "^6.0.0", "pyre-to-regexp": "^0.0.5", diff --git a/client/src/components/Workflow/Invocations.test.js b/client/src/components/Workflow/InvocationsList.test.js similarity index 93% rename from client/src/components/Workflow/Invocations.test.js rename to client/src/components/Workflow/InvocationsList.test.js index ebe93e2bdf8..eba495d7806 100644 --- a/client/src/components/Workflow/Invocations.test.js +++ b/client/src/components/Workflow/InvocationsList.test.js @@ -1,4 +1,4 @@ -import Invocations from "./Invocations"; +import InvocationsList from "./InvocationsList"; import { mount } from "@vue/test-utils"; import axios from "axios"; import MockAdapter from "axios-mock-adapter"; @@ -6,11 +6,14 @@ import { getLocalVue } from "jest/helpers"; import mockInvocationData from "./test/json/invocation.json"; import { parseISO, formatDistanceToNow } from "date-fns"; +import { createPinia } from "pinia"; + import "jest-location-mock"; const localVue = getLocalVue(); -describe("Invocations.vue", () => { +const pinia = createPinia(); +describe("InvocationsList.vue", () => { let axiosMock; let wrapper; @@ -28,7 +31,7 @@ describe("Invocations.vue", () => { const propsData = { ownerGrid: false, }; - wrapper = mount(Invocations, { + wrapper = mount(InvocationsList, { propsData, localVue, }); @@ -49,9 +52,10 @@ describe("Invocations.vue", () => { const propsData = { ownerGrid: false, }; - wrapper = mount(Invocations, { + wrapper = mount(InvocationsList, { propsData, localVue, + pinia, }); }); @@ -68,9 +72,10 @@ describe("Invocations.vue", () => { storedWorkflowName: "My Workflow", storedWorkflowId: "abcde145678", }; - wrapper = mount(Invocations, { + wrapper = mount(InvocationsList, { propsData, localVue, + pinia, }); }); @@ -92,7 +97,7 @@ describe("Invocations.vue", () => { ownerGrid: false, loading: false, }; - wrapper = mount(Invocations, { + wrapper = mount(InvocationsList, { propsData, computed: { getWorkflowNameByInstanceId: (state) => (id) => "workflow name", @@ -113,6 +118,7 @@ describe("Invocations.vue", () => { }, }, localVue, + pinia, }); }); diff --git a/client/src/components/Workflow/Invocations.vue b/client/src/components/Workflow/InvocationsList.vue similarity index 95% rename from client/src/components/Workflow/Invocations.vue rename to client/src/components/Workflow/InvocationsList.vue index 6b010de3d4d..ed1cd2fc9ad 100644 --- a/client/src/components/Workflow/Invocations.vue +++ b/client/src/components/Workflow/InvocationsList.vue @@ -80,14 +80,17 @@ diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationDetails.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationDetails.vue index d6af91920ad..fcf9af8baba 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationDetails.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationDetails.vue @@ -1,3 +1,44 @@ + - diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue index 0336f4f9cc1..67f338450cc 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationStep.vue @@ -84,8 +84,10 @@ diff --git a/client/src/components/providers/storeProviders.js b/client/src/components/providers/storeProviders.js index 441daa77272..f4df9359fd2 100644 --- a/client/src/components/providers/storeProviders.js +++ b/client/src/components/providers/storeProviders.js @@ -1,10 +1,13 @@ // Simple dataset provider, looks at api for result, renders to slot prop import axios from "axios"; import { prependPath } from "utils/redirect"; -import { mapActions, mapGetters } from "vuex"; -import { mapCacheActions } from "vuex-cache"; +import { mapActions as vuexMapActions, mapGetters } from "vuex"; import { HasAttributesMixin } from "./utils"; +import { useDbKeyStore } from "stores/dbKeyStore"; +import { mapActions, mapState } from "pinia"; +import { useDatatypeStore } from "../../stores/datatypeStore"; + export const SimpleProviderMixin = { props: { id: { type: String, required: true }, @@ -57,20 +60,20 @@ export const DbKeyProvider = { await this.load(); }, methods: { - ...mapCacheActions(["fetchUploadDbKeys"]), + ...mapActions(useDbKeyStore, ["fetchUploadDbKeys"]), async load() { this.loading = true; - let dbKeys = this.getUploadDbKeys(); + let dbKeys = this.getUploadDbKeys; if (dbKeys == null || dbKeys.length == 0) { await this.fetchUploadDbKeys(); - dbKeys = this.getUploadDbKeys(); + dbKeys = this.getUploadDbKeys; } this.item = dbKeys; this.loading = false; }, }, computed: { - ...mapGetters(["getUploadDbKeys"]), + ...mapState(useDbKeyStore, ["getUploadDbKeys"]), }, }; @@ -83,20 +86,20 @@ export const DatatypesProvider = { await this.load(); }, methods: { - ...mapCacheActions(["fetchUploadDatatypes"]), + ...mapActions(useDatatypeStore, ["fetchUploadDatatypes"]), async load() { this.loading = true; - let datatypes = this.getUploadDatatypes(); + let datatypes = this.getUploadDatatypes; if (datatypes == null || datatypes.length == 0) { await this.fetchUploadDatatypes(); - datatypes = this.getUploadDatatypes(); + datatypes = this.getUploadDatatypes; } this.item = datatypes; this.loading = false; }, }, computed: { - ...mapGetters(["getUploadDatatypes"]), + ...mapState(useDatatypeStore, ["getUploadDatatypes"]), }, }; @@ -171,7 +174,7 @@ export const StoreProvider = (storeAction, storeGetter, storeCountGetter = undef }); }, methods: { - ...mapActions([storeAction]), + ...vuexMapActions([storeAction]), async load() { this.loading = true; try { diff --git a/client/src/entry/analysis/App.vue b/client/src/entry/analysis/App.vue index 98d6ef750e1..e5e37d851e0 100644 --- a/client/src/entry/analysis/App.vue +++ b/client/src/entry/analysis/App.vue @@ -68,6 +68,12 @@ export default { return { toastRef, confirmDialogRef }; }, + setup() { + const toastRef = ref(null); + setToastComponentRef(toastRef); + + return { toastRef }; + }, data() { return { config: getGalaxyInstance().config, diff --git a/client/src/entry/analysis/index.js b/client/src/entry/analysis/index.js index 6dfb557803d..175a61e15a8 100644 --- a/client/src/entry/analysis/index.js +++ b/client/src/entry/analysis/index.js @@ -4,6 +4,10 @@ import Vue, { provide } from "vue"; import App from "./App.vue"; import store from "store"; import { getRouter } from "./router"; +import { createPinia, PiniaVuePlugin } from "pinia"; + +Vue.use(PiniaVuePlugin); +const pinia = createPinia(); addInitialization((Galaxy) => { console.log("App setup"); @@ -16,6 +20,7 @@ addInitialization((Galaxy) => { render: (h) => h(App), router: router, store: store, + pinia: pinia, }); }); diff --git a/client/src/store/datatypeStore.js b/client/src/store/datatypeStore.js deleted file mode 100644 index 85dadbd4e3d..00000000000 --- a/client/src/store/datatypeStore.js +++ /dev/null @@ -1,34 +0,0 @@ -export const state = { - uploadDatatypes: [], -}; -import UploadUtils from "mvc/upload/upload-utils"; - -const getters = { - getUploadDatatypes: (state) => () => { - return state.uploadDatatypes; - }, -}; - -const actions = { - fetchUploadDatatypes: async ({ commit }) => { - try { - const data = await UploadUtils.getUploadDatatypes(false, UploadUtils.AUTO_EXTENSION); - commit("saveUploadDatatypes", { datatypes: data }); - } catch (err) { - console.log("Error: unable to load datatypes", err); - } - }, -}; - -const mutations = { - saveUploadDatatypes: (state, { datatypes }) => { - state.uploadDatatypes = datatypes; - }, -}; - -export const datatypeStore = { - state, - getters, - actions, - mutations, -}; diff --git a/client/src/store/dbKeyStore.js b/client/src/store/dbKeyStore.js deleted file mode 100644 index b6882ee5b92..00000000000 --- a/client/src/store/dbKeyStore.js +++ /dev/null @@ -1,34 +0,0 @@ -export const state = { - uploadDbKeys: [], -}; -import UploadUtils from "mvc/upload/upload-utils"; - -const getters = { - getUploadDbKeys: (state) => () => { - return state.uploadDbKeys; - }, -}; - -const actions = { - fetchUploadDbKeys: async ({ commit }) => { - try { - const data = await UploadUtils.getUploadDbKeys(UploadUtils.DEFAULT_DBKEY); - commit("saveUploadDbKeys", { dbKeys: data }); - } catch (err) { - console.log("Error: unable to load Database/Builds", err); - } - }, -}; - -const mutations = { - saveUploadDbKeys: (state, { dbKeys }) => { - state.uploadDbKeys = dbKeys; - }, -}; - -export const dbKeyStore = { - state, - getters, - actions, - mutations, -}; diff --git a/client/src/store/index.js b/client/src/store/index.js index bb9dc60f12e..c65b03a90bd 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -18,14 +18,11 @@ import { invocationStore } from "./invocationStore"; import { collectionElementsStore, datasetStore, historyItemsStore, historyStore } from "./historyStore"; import { userStore, userFlagsStore } from "./userStore"; import { configStore } from "./configStore"; -import { workflowStore } from "./workflowStore"; import { toolStore } from "./toolStore"; import { datasetPathDestinationStore } from "./datasetPathDestinationStore"; import { datasetExtFilesStore } from "./datasetExtFilesStore"; import { jobStore } from "./jobStore"; import { collectionAttributesStore } from "./collectionAttributesStore"; -import { dbKeyStore } from "./dbKeyStore"; -import { datatypeStore } from "./datatypeStore"; import { panelStore } from "./panelStore"; // Syncs vuex to Galaxy store until Galaxy vals to not exist @@ -65,11 +62,9 @@ export function createStore() { dataset: datasetStore, datasetExtFiles: datasetExtFilesStore, datasetPathDestination: datasetPathDestinationStore, - datatypeStore: datatypeStore, informationStore: jobStore, invocations: invocationStore, jobMetrics: jobMetricsStore, - dbkeyStore: dbKeyStore, gridSearch: gridSearchStore, history: historyStore, historyItems: historyItemsStore, @@ -78,7 +73,6 @@ export function createStore() { tools: toolStore, user: userStore, userFlags: userFlagsStore, - workflows: workflowStore, }, }; diff --git a/client/src/store/workflowStore.js b/client/src/store/workflowStore.js deleted file mode 100644 index 9bec25bf9a1..00000000000 --- a/client/src/store/workflowStore.js +++ /dev/null @@ -1,46 +0,0 @@ -export const state = { - workflowsByInstanceId: {}, -}; - -import Vue from "vue"; -import { getAppRoot } from "onload/loadConfig"; -import axios from "axios"; - -const getters = { - getWorkflowByInstanceId: (state) => (workflowId) => { - return state.workflowsByInstanceId[workflowId]; - }, - getWorkflowNameByInstanceId: (state) => (workflowId) => { - const details = state.workflowsByInstanceId[workflowId]; - if (details && details.name) { - return details.name; - } else { - return "..."; - } - }, - getStoredWorkflowIdByInstanceId: (state) => (workflowId) => { - const storedWorkflow = state.workflowsByInstanceId[workflowId]; - return storedWorkflow?.id; - }, -}; - -const actions = { - fetchWorkflowForInstanceId: async ({ commit }, workflowId) => { - const params = { instance: "true" }; - const { data } = await axios.get(`${getAppRoot()}api/workflows/${workflowId}`, { params }); - commit("saveWorkflowForInstanceId", { workflowId, workflowData: data }); - }, -}; - -const mutations = { - saveWorkflowForInstanceId: (state, { workflowId, workflowData }) => { - Vue.set(state.workflowsByInstanceId, workflowId, workflowData); - }, -}; - -export const workflowStore = { - state, - getters, - actions, - mutations, -}; diff --git a/client/src/stores/datatypeStore.js b/client/src/stores/datatypeStore.js new file mode 100644 index 00000000000..e785e35f848 --- /dev/null +++ b/client/src/stores/datatypeStore.js @@ -0,0 +1,23 @@ +import { defineStore } from "pinia"; +import UploadUtils from "mvc/upload/upload-utils"; + +export const useDatatypeStore = defineStore("datatypeStore", { + state: () => ({ + uploadDatatypes: [], + }), + getters: { + getUploadDatatypes: (state) => { + return state.uploadDatatypes; + }, + }, + actions: { + async fetchUploadDatatypes() { + try { + const data = await UploadUtils.getUploadDatatypes(false, UploadUtils.AUTO_EXTENSION); + this.uploadDatatypes = data; + } catch (err) { + console.log("Error: unable to load datatypes", err); + } + }, + }, +}); diff --git a/client/src/stores/dbKeyStore.js b/client/src/stores/dbKeyStore.js new file mode 100644 index 00000000000..1dadf6d0c28 --- /dev/null +++ b/client/src/stores/dbKeyStore.js @@ -0,0 +1,23 @@ +import { defineStore } from "pinia"; +import UploadUtils from "mvc/upload/upload-utils"; + +export const useDbKeyStore = defineStore("dbKeyStore", { + state: () => ({ + uploadDbKeys: [], + }), + getters: { + getUploadDbKeys: (state) => { + return state.uploadDbKeys; + }, + }, + actions: { + async fetchUploadDbKeys() { + try { + const data = await UploadUtils.getUploadDbKeys(UploadUtils.DEFAULT_DBKEY); + this.uploadDbKeys = data; + } catch (err) { + console.log("Error: unable to load Database/Builds", err); + } + }, + }, +}); diff --git a/client/src/stores/workflowStore.js b/client/src/stores/workflowStore.js new file mode 100644 index 00000000000..5ba6400e1fe --- /dev/null +++ b/client/src/stores/workflowStore.js @@ -0,0 +1,43 @@ +import { defineStore } from "pinia"; +import axios from "axios"; + +import { getAppRoot } from "onload/loadConfig"; + +export const useWorkflowStore = defineStore("workflowStore", { + state: () => ({ + workflowsByInstanceId: {}, + }), + getters: { + getWorkflowByInstanceId: (state) => { + return (workflowId) => { + state.workflowsByInstanceId[workflowId]; + }; + }, + getWorkflowNameByInstanceId: (state) => { + return (workflowId) => { + const details = state.workflowsByInstanceId[workflowId]; + if (details && details.name) { + return details.name; + } else { + return "..."; + } + }; + }, + getStoredWorkflowIdByInstanceId: (state) => { + return (workflowId) => { + const storedWorkflow = state.workflowsByInstanceId[workflowId]; + return storedWorkflow?.id; + }; + }, + }, + actions: { + async fetchWorkflowForInstanceId(workflowId) { + console.debug("Fetching workflow details for", workflowId); + const params = { instance: "true" }; + const { data } = await axios.get(`${getAppRoot()}api/workflows/${workflowId}`, { params }); + this.$patch((state) => { + state.workflowsByInstanceId[workflowId] = data; + }); + }, + }, +}); diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index 27f42d756f4..429f3702e9d 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -11,6 +11,7 @@ import { iconPlugin } from "components/plugins/icons"; import BootstrapVue from "bootstrap-vue"; import Vuex from "vuex"; import _l from "utils/localization"; +import { PiniaVuePlugin } from "pinia"; const defaultComparator = (a, b) => a == b; @@ -153,6 +154,7 @@ export function getLocalVue(instrumentLocalization = false) { const mockedDirective = { bind() {}, }; + localVue.use(PiniaVuePlugin); localVue.use(Vuex); localVue.use(BootstrapVue); const l = instrumentLocalization ? testLocalize : _l; diff --git a/client/yarn.lock b/client/yarn.lock index f93c6060e63..254ae707135 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2191,6 +2191,11 @@ optionalDependencies: prettier "^1.18.2 || ^2.0.0" +"@vue/devtools-api@^6.4.4": + version "6.4.4" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.4.tgz#0b024fc8ca91bb4b6035abaf53c5aecc17119b3b" + integrity sha512-Ku31WzpOV/8cruFaXaEZKF81WkNnvCSlBY4eOGtz5WMSdJvX1v1WWlSMGZeqUwPtQ27ZZz7B62erEMq8JDjcXw== + "@vue/test-utils@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.3.0.tgz#d563decdcd9c68a7bca151d4179a2bfd6d5c3e15" @@ -8073,6 +8078,14 @@ pikaday@1.5.1: optionalDependencies: moment "2.x" +pinia@^2.0.23: + version "2.0.23" + resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.23.tgz#570f5f82160b656b412602789683faa95502d227" + integrity sha512-N15hFf4o5STrxpNrib1IEb1GOArvPYf1zPvQVRGOO1G1d74Ak0J0lVyalX/SmrzdT4Q0nlEFjbURsmBmIGUR5Q== + dependencies: + "@vue/devtools-api" "^6.4.4" + vue-demi "*" + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -10314,6 +10327,11 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== +vue-demi@*: + version "0.13.6" + resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.6.tgz#f9433cbd75e68a970dec066647f4ba6c08ced48f" + integrity sha512-02NYpxgyGE2kKGegRPYlNQSL1UWfA/+JqvzhGCOYjhfbLWXU5QQX0+9pAm/R2sCOPKr5NBxVIab7fvFU0B1RxQ== + vue-eslint-parser@^9.0.1: version "9.0.3" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz#0c17a89e0932cc94fa6a79f0726697e13bfe3c96"