Merge pull request #14999 from dannon/pinia-initial-swap

Pinia addition and initial conversion from vuex for a few basic stores.
This commit is contained in:
Aysam Guerler
2022-11-21 15:34:54 -05:00
committed by GitHub
20 changed files with 217 additions and 201 deletions
+1
View File
@@ -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",
@@ -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,
});
});
@@ -80,14 +80,17 @@
</template>
<script>
import { mapCacheActions } from "vuex-cache";
import { mapGetters } from "vuex";
import { mapActions, mapState } from "pinia";
import { getAppRoot } from "onload/loadConfig";
import { getGalaxyInstance } from "app";
import { invocationsProvider } from "components/providers/InvocationsProvider";
import WorkflowInvocationState from "components/WorkflowInvocationState/WorkflowInvocationState";
import WorkflowRunButton from "./WorkflowRunButton.vue";
import UtcDate from "components/UtcDate";
import { mapCacheActions } from "vuex-cache";
import { mapGetters } from "vuex";
import { useWorkflowStore } from "stores/workflowStore";
import paginationMixin from "./paginationMixin";
export default {
@@ -125,7 +128,11 @@ export default {
};
},
computed: {
...mapGetters(["getWorkflowNameByInstanceId", "getWorkflowByInstanceId", "getStoredWorkflowIdByInstanceId"]),
...mapState(useWorkflowStore, [
"getWorkflowNameByInstanceId",
"getWorkflowByInstanceId",
"getStoredWorkflowIdByInstanceId",
]),
...mapGetters("history", ["getHistoryById", "getHistoryNameById"]),
title() {
let title = `Workflow Invocations`;
@@ -160,7 +167,7 @@ export default {
},
},
methods: {
...mapCacheActions(["fetchWorkflowForInstanceId"]),
...mapActions(useWorkflowStore, ["fetchWorkflowForInstanceId"]),
...mapCacheActions("history", ["loadHistoryById"]),
async provider(ctx) {
ctx.root = this.root;
@@ -1,7 +1,7 @@
<template>
<CurrentUser v-slot="{ user }">
<StoredWorkflowDetailsProvider v-slot="{ result: item, loading }" :stored-workflow-id="storedWorkflowId">
<Invocations
<InvocationsList
v-if="!loading && user.id"
:user-id="user.id"
:stored-workflow-id="item.id"
@@ -10,14 +10,14 @@
</CurrentUser>
</template>
<script>
import Invocations from "components/Workflow/Invocations";
import InvocationsList from "components/Workflow/InvocationsList";
import CurrentUser from "components/providers/CurrentUser";
import { StoredWorkflowDetailsProvider } from "components/providers/StoredWorkflowsProvider";
export default {
components: {
CurrentUser,
Invocations,
InvocationsList,
StoredWorkflowDetailsProvider,
},
props: {
@@ -1,15 +1,15 @@
<template>
<!-- Bit of an odd component, but the API returns all invocations for admins if no user id is included in request-->
<CurrentUser v-slot="{ user }"> <Invocations v-if="user.id" :user-id="user.id" /> </CurrentUser>
<CurrentUser v-slot="{ user }"> <InvocationsList v-if="user.id" :user-id="user.id" /> </CurrentUser>
</template>
<script>
import Invocations from "components/Workflow/Invocations";
import InvocationsList from "components/Workflow/InvocationsList";
import CurrentUser from "components/providers/CurrentUser";
export default {
components: {
CurrentUser,
Invocations,
InvocationsList,
},
};
</script>
@@ -1,3 +1,44 @@
<script setup>
import { computed, onMounted } from "vue";
import { useWorkflowStore } from "stores/workflowStore";
import ParameterStep from "./ParameterStep";
import GenericHistoryItem from "components/History/Content/GenericItem";
import WorkflowInvocationStep from "./WorkflowInvocationStep";
const props = defineProps({
invocation: {
type: Object,
required: true,
},
});
const workflowStore = useWorkflowStore();
const workflow = computed(() => {
return workflowStore.workflowsByInstanceId[props.invocation.workflow_id];
});
function dataInputStepLabel(key, input) {
const invocationStep = props.invocation.steps[key];
let label = invocationStep && invocationStep.workflow_step_label;
if (!label) {
if (input.src === "hda" || input.src === "ldda") {
label = "Input dataset";
} else if (input.src === "hdca") {
label = "Input dataset collection";
}
}
return label;
}
onMounted(async () => {
if (!workflowStore.workflowsByInstanceId[props.invocation.workflow_id]) {
workflowStore.fetchWorkflowForInstanceId(props.invocation.workflow_id);
}
});
</script>
<template>
<div v-if="invocation">
<b-tabs lazy>
@@ -36,49 +77,3 @@
</b-tabs>
</div>
</template>
<script>
import ParameterStep from "./ParameterStep";
import GenericHistoryItem from "components/History/Content/GenericItem";
import WorkflowInvocationStep from "./WorkflowInvocationStep";
import { mapGetters } from "vuex";
import { mapCacheActions } from "vuex-cache";
export default {
components: {
GenericHistoryItem,
WorkflowInvocationStep,
ParameterStep,
},
props: {
invocation: {
type: Object,
required: true,
},
},
computed: {
...mapGetters(["getWorkflowByInstanceId"]),
workflow() {
return this.getWorkflowByInstanceId(this.invocation.workflow_id);
},
},
created: function () {
this.fetchWorkflowForInstanceId(this.invocation.workflow_id);
},
methods: {
...mapCacheActions(["fetchWorkflowForInstanceId"]),
dataInputStepLabel(key, input) {
const invocationStep = this.invocation.steps[key];
let label = invocationStep && invocationStep.workflow_step_label;
if (!label) {
if (input.src === "hda" || input.src === "ldda") {
label = "Input dataset";
} else if (input.src === "hdca") {
label = "Input dataset collection";
}
}
return label;
},
},
};
</script>
@@ -84,8 +84,10 @@
</div>
</template>
<script>
import { useWorkflowStore } from "stores/workflowStore";
import { mapCacheActions } from "vuex-cache";
import { mapGetters, mapActions } from "vuex";
import { mapGetters, mapActions as vuexMapActions } from "vuex";
import { mapState, mapActions } from "pinia";
import WorkflowIcons from "components/Workflow/icons";
import JobStep from "./JobStep";
import ParameterStep from "./ParameterStep";
@@ -114,7 +116,8 @@ export default {
};
},
computed: {
...mapGetters(["getToolForId", "getToolNameById", "getWorkflowByInstanceId", "getInvocationStepById"]),
...mapState(useWorkflowStore, ["getWorkflowByInstanceId"]),
...mapGetters(["getToolForId", "getToolNameById", "getInvocationStepById"]),
isReady() {
return this.invocation.steps.length > 0;
},
@@ -142,8 +145,9 @@ export default {
this.fetchSubworkflow();
},
methods: {
...mapCacheActions(["fetchToolForId", "fetchWorkflowForInstanceId"]),
...mapActions(["fetchInvocationStepById"]),
...mapCacheActions(["fetchToolForId"]),
...mapActions(useWorkflowStore, ["fetchWorkflowForInstanceId"]),
...vuexMapActions(["fetchInvocationStepById"]),
fetchTool() {
if (this.workflowStep.tool_id && !this.getToolForId(this.workflowStep.tool_id)) {
this.fetchToolForId(this.workflowStep.tool_id);
@@ -1,17 +1,17 @@
<template>
<invocations
<InvocationsList
header-message="Workflow invocations that are still being scheduled are displayed on this page."
no-invocations-message="There are no scheduling workflow invocations to show currently."
:owner-grid="false">
</invocations>
</InvocationsList>
</template>
<script>
import Invocations from "../Workflow/Invocations";
import InvocationsList from "../Workflow/InvocationsList";
export default {
components: {
Invocations,
InvocationsList,
},
};
</script>
@@ -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 {
+6
View File
@@ -68,6 +68,12 @@ export default {
return { toastRef, confirmDialogRef };
},
setup() {
const toastRef = ref(null);
setToastComponentRef(toastRef);
return { toastRef };
},
data() {
return {
config: getGalaxyInstance().config,
+5
View File
@@ -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,
});
});
-34
View File
@@ -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,
};
-34
View File
@@ -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,
};
-6
View File
@@ -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,
},
};
-46
View File
@@ -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,
};
+23
View File
@@ -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);
}
},
},
});
+23
View File
@@ -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);
}
},
},
});
+43
View File
@@ -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;
});
},
},
});
+2
View File
@@ -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;
+18
View File
@@ -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"