mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'release_23.2' into dev
This commit is contained in:
+85
-41
@@ -29,8 +29,14 @@ const TASKS_CONFIG = {
|
||||
enable_celery_tasks: true,
|
||||
};
|
||||
|
||||
const getPurgedContentSelection = () => new Map([["FAKE_ID", { purged: true }]]);
|
||||
const getNonPurgedContentSelection = () => new Map([["FAKE_ID", { purged: false }]]);
|
||||
const getMenuSelectorFor = (option) => `[data-description="${option} option"]`;
|
||||
|
||||
const getPurgedSelection = () => new Map([["FAKE_ID", { purged: true }]]);
|
||||
const getNonPurgedSelection = () => new Map([["FAKE_ID", { purged: false }]]);
|
||||
const getVisibleSelection = () => new Map([["FAKE_ID", { visible: true }]]);
|
||||
const getHiddenSelection = () => new Map([["FAKE_ID", { visible: false }]]);
|
||||
const getDeletedSelection = () => new Map([["FAKE_ID", { deleted: true }]]);
|
||||
const getActiveSelection = () => new Map([["FAKE_ID", { deleted: false }]]);
|
||||
|
||||
async function mountSelectionOperationsWrapper(config) {
|
||||
mockFetcher.path("/api/configuration").method("get").mock({ data: config });
|
||||
@@ -77,117 +83,155 @@ describe("History Selection Operations", () => {
|
||||
expect(wrapper.find('[data-description="selected count"]').text()).toContain("10");
|
||||
});
|
||||
|
||||
it("should display 'hide' option only on visible items", async () => {
|
||||
const option = '[data-description="hide option"]';
|
||||
it("should display 'hide' option on visible items", async () => {
|
||||
const option = getMenuSelectorFor("hide");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "visible:true" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'hide' option when visible and hidden items are mixed", async () => {
|
||||
const option = getMenuSelectorFor("hide");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "visible:any" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should not display 'hide' option when only hidden items are selected", async () => {
|
||||
const option = getMenuSelectorFor("hide");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "visible:any", contentSelection: getHiddenSelection() });
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
await wrapper.setProps({ filterText: "visible:false" });
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("should display 'unhide' option on hidden items", async () => {
|
||||
const option = '[data-description="unhide option"]';
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
const option = getMenuSelectorFor("unhide");
|
||||
await wrapper.setProps({ filterText: "visible:false" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'unhide' option when hidden and visible items are mixed", async () => {
|
||||
const option = '[data-description="unhide option"]';
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
const option = getMenuSelectorFor("unhide");
|
||||
await wrapper.setProps({ filterText: "visible:any" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'delete' option on non-deleted items", async () => {
|
||||
const option = '[data-description="delete option"]';
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:true" });
|
||||
it("should not display 'unhide' option when only visible items are selected", async () => {
|
||||
const option = getMenuSelectorFor("unhide");
|
||||
await wrapper.setProps({
|
||||
filterText: "visible:any",
|
||||
contentSelection: getVisibleSelection(),
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("should display 'delete' option on non-deleted items", async () => {
|
||||
const option = getMenuSelectorFor("delete");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:false" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'delete' option on non-deleted items", async () => {
|
||||
const option = getMenuSelectorFor("delete");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:false" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'delete' option when non-deleted and deleted items are mixed", async () => {
|
||||
const option = '[data-description="delete option"]';
|
||||
const option = getMenuSelectorFor("delete");
|
||||
await wrapper.setProps({ filterText: "deleted:any" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'permanently delete' option always", async () => {
|
||||
const option = '[data-description="purge option"]';
|
||||
it("should not display 'delete' option when only deleted items are selected", async () => {
|
||||
const option = getMenuSelectorFor("delete");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:true" });
|
||||
await wrapper.setProps({ filterText: "deleted:any", contentSelection: getDeletedSelection() });
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("should display 'permanently delete' option always", async () => {
|
||||
const option = getMenuSelectorFor("purge");
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:any visible:any" });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'undelete' option on deleted and non-purged items", async () => {
|
||||
const option = '[data-description="undelete option"]';
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
await wrapper.setProps({
|
||||
filterText: "deleted:true",
|
||||
contentSelection: getNonPurgedContentSelection(),
|
||||
contentSelection: getNonPurgedSelection(),
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'undelete' option when non-purged items (deleted or not) are mixed", async () => {
|
||||
const option = '[data-description="undelete option"]';
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
await wrapper.setProps({
|
||||
filterText: "deleted:any",
|
||||
contentSelection: getNonPurgedContentSelection(),
|
||||
contentSelection: getNonPurgedSelection(),
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should not display 'undelete' when is manual selection mode and all selected items are purged", async () => {
|
||||
const option = '[data-description="undelete option"]';
|
||||
it("should not display 'undelete' when only non-deleted items are selected", async () => {
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
await wrapper.setProps({
|
||||
filterText: "deleted:true",
|
||||
contentSelection: getPurgedContentSelection(),
|
||||
filterText: "deleted:any",
|
||||
contentSelection: getActiveSelection(),
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("should not display 'undelete' when only purged items are selected", async () => {
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
await wrapper.setProps({
|
||||
contentSelection: getPurgedSelection(),
|
||||
isQuerySelection: false,
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("should display 'undelete' option when is query selection mode and filtering by deleted", async () => {
|
||||
const option = '[data-description="undelete option"]';
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
// In query selection mode we don't know if some items may not be purged, so we allow to undelete
|
||||
await wrapper.setProps({
|
||||
filterText: "deleted:true",
|
||||
contentSelection: getPurgedContentSelection(),
|
||||
contentSelection: getPurgedSelection(),
|
||||
isQuerySelection: true,
|
||||
});
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display 'undelete' option when is query selection mode and filtering by any deleted state", async () => {
|
||||
const option = '[data-description="undelete option"]';
|
||||
const option = getMenuSelectorFor("undelete");
|
||||
// In query selection mode we don't know if some items may not be purged, so we allow to undelete
|
||||
await wrapper.setProps({
|
||||
filterText: "deleted:any",
|
||||
contentSelection: getPurgedContentSelection(),
|
||||
isQuerySelection: true,
|
||||
});
|
||||
await wrapper.setProps({ filterText: "deleted:any", isQuerySelection: true });
|
||||
expect(wrapper.find(option).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("should display collection building options only on visible and non-deleted items", async () => {
|
||||
it("should display collection building options only on active (non-deleted) items", async () => {
|
||||
const buildListOption = '[data-description="build list"]';
|
||||
const buildPairOption = '[data-description="build pair"]';
|
||||
const buildListOfPairsOption = '[data-description="build list of pairs"]';
|
||||
await wrapper.setProps({ filterText: "visible:true deleted:false" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(true);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(true);
|
||||
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "visible:false" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
|
||||
await wrapper.setProps({ filterText: "deleted:true" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
|
||||
await wrapper.setProps({ filterText: "visible:any" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
|
||||
await wrapper.setProps({ filterText: "visible:any deleted:false" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(true);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(true);
|
||||
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(true);
|
||||
await wrapper.setProps({ filterText: "deleted:any" });
|
||||
expect(wrapper.find(buildListOption).exists()).toBe(false);
|
||||
expect(wrapper.find(buildPairOption).exists()).toBe(false);
|
||||
|
||||
+54
-11
@@ -12,10 +12,10 @@
|
||||
<b-dropdown-text>
|
||||
<span v-localize data-description="selected count">With {{ numSelected }} selected...</span>
|
||||
</b-dropdown-text>
|
||||
<b-dropdown-item v-if="showHidden" v-b-modal:show-selected-content data-description="unhide option">
|
||||
<b-dropdown-item v-if="canUnhideSelection" v-b-modal:show-selected-content data-description="unhide option">
|
||||
<span v-localize>Unhide</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-else v-b-modal:hide-selected-content data-description="hide option">
|
||||
<b-dropdown-item v-if="canHideSelection" v-b-modal:hide-selected-content data-description="hide option">
|
||||
<span v-localize>Hide</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
@@ -25,7 +25,7 @@
|
||||
<span v-localize>Undelete</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item
|
||||
v-if="!showStrictDeleted"
|
||||
v-if="canDeleteSelection"
|
||||
v-b-modal:delete-selected-content
|
||||
data-description="delete option">
|
||||
<span v-localize>Delete</span>
|
||||
@@ -193,20 +193,28 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
/** @returns {Boolean} */
|
||||
showHidden() {
|
||||
return !HistoryFilters.checkFilter(this.filterText, "visible", true);
|
||||
canUnhideSelection() {
|
||||
return this.areAllSelectedHidden || (this.isAnyVisibilityAllowed && !this.areAllSelectedVisible);
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
canHideSelection() {
|
||||
return this.areAllSelectedVisible || (this.isAnyVisibilityAllowed && !this.areAllSelectedHidden);
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
showDeleted() {
|
||||
return !HistoryFilters.checkFilter(this.filterText, "deleted", false);
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
showStrictDeleted() {
|
||||
return HistoryFilters.checkFilter(this.filterText, "deleted", true);
|
||||
canDeleteSelection() {
|
||||
return this.areAllSelectedActive || (this.isAnyDeletedStateAllowed && !this.areAllSelectedDeleted);
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
canUndeleteSelection() {
|
||||
return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
showBuildOptions() {
|
||||
return !this.isQuerySelection && !this.showHidden && !this.showDeleted;
|
||||
return !this.isQuerySelection && this.areAllSelectedActive && !this.showDeleted;
|
||||
},
|
||||
/** @returns {Boolean} */
|
||||
showBuildOptionForAll() {
|
||||
@@ -227,9 +235,6 @@ export default {
|
||||
noTagsSelected() {
|
||||
return this.selectedTags.length === 0;
|
||||
},
|
||||
canUndeleteSelection() {
|
||||
return this.showDeleted && (this.isQuerySelection || !this.areAllSelectedPurged);
|
||||
},
|
||||
areAllSelectedPurged() {
|
||||
for (const item of this.contentSelection.values()) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "purged") && !item["purged"]) {
|
||||
@@ -238,6 +243,44 @@ export default {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
areAllSelectedVisible() {
|
||||
for (const item of this.contentSelection.values()) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "visible") && !item["visible"]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
areAllSelectedHidden() {
|
||||
for (const item of this.contentSelection.values()) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "visible") && item["visible"]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
areAllSelectedActive() {
|
||||
for (const item of this.contentSelection.values()) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "deleted") && item["deleted"]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
areAllSelectedDeleted() {
|
||||
for (const item of this.contentSelection.values()) {
|
||||
if (Object.prototype.hasOwnProperty.call(item, "deleted") && !item["deleted"]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
isAnyVisibilityAllowed() {
|
||||
return HistoryFilters.checkFilter(this.filterText, "visible", "any");
|
||||
},
|
||||
isAnyDeletedStateAllowed() {
|
||||
return HistoryFilters.checkFilter(this.filterText, "deleted", "any");
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
hasSelection(newVal) {
|
||||
|
||||
@@ -52,6 +52,7 @@ export default {
|
||||
props: {
|
||||
creators: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@@ -61,8 +62,11 @@ export default {
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
creators() {
|
||||
this.creatorsCurrent = this.creators;
|
||||
creators: {
|
||||
handler(newCreators) {
|
||||
this.creatorsCurrent = newCreators;
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -97,7 +97,7 @@ export default {
|
||||
default: "",
|
||||
},
|
||||
creator: {
|
||||
type: Object,
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
version: {
|
||||
|
||||
@@ -165,7 +165,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import { Toast } from "composables/toast";
|
||||
import { storeToRefs } from "pinia";
|
||||
import Vue, { computed, onUnmounted, ref, unref } from "vue";
|
||||
@@ -179,9 +178,11 @@ import { hide_modal } from "@/layout/modal";
|
||||
import { getAppRoot } from "@/onload/loadConfig";
|
||||
import { useScopePointerStore } from "@/stores/scopePointerStore";
|
||||
import { LastQueue } from "@/utils/lastQueue";
|
||||
import { errorMessageAsString } from "@/utils/simple-error";
|
||||
|
||||
import { Services } from "../services";
|
||||
import { defaultPosition } from "./composables/useDefaultStepPosition";
|
||||
import { fromSimple, toSimple } from "./modules/model";
|
||||
import { fromSimple } from "./modules/model";
|
||||
import { getModule, getVersions, loadWorkflow, saveWorkflow } from "./modules/services";
|
||||
import { getStateUpgradeMessages } from "./modules/utilities";
|
||||
import reportDefault from "./reportDefault";
|
||||
@@ -275,6 +276,7 @@ export default {
|
||||
});
|
||||
|
||||
function resetStores() {
|
||||
hasChanges.value = false;
|
||||
connectionStore.$reset();
|
||||
stepStore.$reset();
|
||||
stateStore.$reset();
|
||||
@@ -319,6 +321,7 @@ export default {
|
||||
annotation: null,
|
||||
name: "Unnamed Workflow",
|
||||
tags: this.workflowTags,
|
||||
services: null,
|
||||
stateMessages: [],
|
||||
insertedStateMessages: [],
|
||||
refactorActions: [],
|
||||
@@ -382,6 +385,7 @@ export default {
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.services = new Services();
|
||||
this.lastQueue = new LastQueue();
|
||||
await this._loadCurrent(this.id, this.version);
|
||||
hide_modal();
|
||||
@@ -533,30 +537,27 @@ export default {
|
||||
onDownload() {
|
||||
window.location = `${getAppRoot()}api/workflows/${this.id}/download?format=json-download`;
|
||||
},
|
||||
async doSaveAs(create = false) {
|
||||
const rename_name = create ? this.name : this.saveAsName ?? `SavedAs_${this.name}`;
|
||||
const rename_annotation = create ? this.annotation || "" : this.saveAsAnnotation ?? "";
|
||||
|
||||
// This is an old web controller endpoint that wants form data posted...
|
||||
const formData = new FormData();
|
||||
formData.append("workflow_name", rename_name);
|
||||
formData.append("workflow_annotation", rename_annotation);
|
||||
formData.append("from_tool_form", true);
|
||||
formData.append("workflow_data", JSON.stringify(toSimple(this.id, this)));
|
||||
async doSaveAs() {
|
||||
if (!this.saveAsName && !this.nameValidate()) {
|
||||
return;
|
||||
}
|
||||
const rename_name = this.saveAsName ?? `SavedAs_${this.name}`;
|
||||
const rename_annotation = this.saveAsAnnotation ?? "";
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${getAppRoot()}workflow/save_workflow_as`, formData);
|
||||
const newId = response.data;
|
||||
|
||||
if (!create) {
|
||||
this.name = rename_name;
|
||||
this.annotation = rename_annotation;
|
||||
}
|
||||
|
||||
const newSaveAsWf = { ...this, name: rename_name, annotation: rename_annotation };
|
||||
const { id, name, number_of_steps } = await this.services.createWorkflow(newSaveAsWf);
|
||||
const message = `Created new workflow '${name}' with ${number_of_steps} steps.`;
|
||||
this.hasChanges = false;
|
||||
await this.routeToWorkflow(newId);
|
||||
await this.routeToWorkflow(id);
|
||||
Toast.success(message);
|
||||
} catch (e) {
|
||||
this.onWorkflowError("Saving workflow failed, please contact an administrator.");
|
||||
const errorHeading = `Saving workflow as '${rename_name}' failed`;
|
||||
this.onWorkflowError(errorHeading, errorMessageAsString(e) || "Please contact an administrator.", {
|
||||
Ok: () => {
|
||||
this.hideModal();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
onSaveAs() {
|
||||
@@ -597,34 +598,21 @@ export default {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// if nothing other than payload vars changed, just use `create` endpoint
|
||||
if (!this.hasChanges) {
|
||||
const payload = {
|
||||
workflow_name: this.name,
|
||||
workflow_annotation: this.annotation || "",
|
||||
workflow_tags: this.tags,
|
||||
};
|
||||
const { data } = await axios.put(`${getAppRoot()}workflow/create`, payload);
|
||||
const { id, message } = data;
|
||||
|
||||
await this.routeToWorkflow(id);
|
||||
Toast.success(message);
|
||||
} else {
|
||||
// otherwise, use `save_as` endpoint to include steps, etc.
|
||||
await this.doSaveAs(true);
|
||||
const stepCount = Object.keys(this.steps).length;
|
||||
Toast.success(
|
||||
`Created workflow ${this.name} with ${stepCount} ${stepCount === 1 ? "step" : "steps"}.`
|
||||
);
|
||||
}
|
||||
const { id, name, number_of_steps } = await this.services.createWorkflow(this);
|
||||
const message = `Created new workflow '${name}' with ${number_of_steps} steps.`;
|
||||
this.hasChanges = false;
|
||||
await this.routeToWorkflow(id);
|
||||
Toast.success(message);
|
||||
} catch (e) {
|
||||
this.onWorkflowError("Creating workflow failed"),
|
||||
e || "Please contact an administrator.",
|
||||
this.onWorkflowError(
|
||||
"Creating workflow failed",
|
||||
errorMessageAsString(e) || "Please contact an administrator.",
|
||||
{
|
||||
Ok: () => {
|
||||
this.hideModal();
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
nameValidate() {
|
||||
|
||||
@@ -3,6 +3,8 @@ import axios from "axios";
|
||||
import { withPrefix } from "utils/redirect";
|
||||
import { rethrowSimple } from "utils/simple-error";
|
||||
|
||||
import { toSimple } from "./Editor/modules/model";
|
||||
|
||||
/** Workflow data request helper **/
|
||||
export class Services {
|
||||
async copyWorkflow(workflow) {
|
||||
@@ -27,6 +29,16 @@ export class Services {
|
||||
}
|
||||
}
|
||||
|
||||
async createWorkflow(workflow) {
|
||||
const url = withPrefix("/api/workflows");
|
||||
try {
|
||||
const { data } = await axios.post(url, { workflow: toSimple(workflow.id, workflow) });
|
||||
return data;
|
||||
} catch (e) {
|
||||
rethrowSimple(e);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteWorkflow(id) {
|
||||
const url = withPrefix(`/api/workflows/${id}`);
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import abc
|
||||
import os
|
||||
import time
|
||||
from dataclasses import (
|
||||
dataclass,
|
||||
field,
|
||||
)
|
||||
from enum import Enum
|
||||
from typing import (
|
||||
Any,
|
||||
@@ -88,19 +92,20 @@ class FilesSourceProperties(TypedDict):
|
||||
browsable: NotRequired[bool]
|
||||
|
||||
|
||||
@dataclass
|
||||
class FilesSourceOptions:
|
||||
"""Options to control behavior of file source operations, such as realize_to, write_from and list."""
|
||||
|
||||
# Indicates access to the FS operation with intent to write.
|
||||
# Even if a file source is "writeable" some directories (or elements) may be restricted or read-only
|
||||
# so those should be skipped while browsing with writeable=True.
|
||||
writeable: Optional[bool]
|
||||
writeable: Optional[bool] = False
|
||||
|
||||
# Property overrides for values initially configured through the constructor. For example
|
||||
# the HTTPFilesSource passes in additional http_headers through these properties, which
|
||||
# are merged with constructor defined http_headers. The interpretation of these properties
|
||||
# are filesystem specific.
|
||||
extra_props: Optional[FilesSourceProperties]
|
||||
extra_props: Optional[FilesSourceProperties] = field(default_factory=lambda: FilesSourceProperties())
|
||||
|
||||
|
||||
class EntryData(TypedDict):
|
||||
|
||||
@@ -1244,15 +1244,13 @@ class MinimalJobWrapper(HasResourceParameters):
|
||||
|
||||
tool_evaluator = self._get_tool_evaluator(job)
|
||||
compute_environment = compute_environment or self.default_compute_environment(job)
|
||||
if hasattr(self.app, "interactivetool_manager"):
|
||||
self.interactivetools = tool_evaluator.populate_interactivetools()
|
||||
self.app.interactivetool_manager.create_interactivetool(job, self.tool, self.interactivetools)
|
||||
tool_evaluator.set_compute_environment(compute_environment, get_special=get_special)
|
||||
(
|
||||
self.command_line,
|
||||
self.version_command_line,
|
||||
self.extra_filenames,
|
||||
self.environment_variables,
|
||||
self.interactivetools,
|
||||
) = tool_evaluator.build()
|
||||
job.command_line = self.command_line
|
||||
|
||||
@@ -2616,6 +2614,7 @@ class TaskWrapper(JobWrapper):
|
||||
self.version_command_line,
|
||||
extra_filenames,
|
||||
self.environment_variables,
|
||||
*_,
|
||||
) = tool_evaluator.build()
|
||||
self.extra_filenames.extend(extra_filenames)
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ class ToolEvaluator:
|
||||
self.environment_variables: List[Dict[str, str]] = []
|
||||
self.version_command_line: Optional[str] = None
|
||||
self.command_line: Optional[str] = None
|
||||
self.interactivetools: List[Dict[str, Any]] = []
|
||||
|
||||
def set_compute_environment(self, compute_environment: ComputeEnvironment, get_special: Optional[Callable] = None):
|
||||
"""
|
||||
@@ -514,7 +515,12 @@ class ToolEvaluator:
|
||||
# the paths rewritten.
|
||||
self.__walk_inputs(self.tool.inputs, param_dict, rewrite_unstructured_paths)
|
||||
|
||||
def populate_interactivetools(self):
|
||||
def _create_interactivetools_entry_points(self):
|
||||
if hasattr(self.app, "interactivetool_manager"):
|
||||
self.interactivetools = self._populate_interactivetools_template()
|
||||
self.app.interactivetool_manager.create_interactivetool(self.job, self.tool, self.interactivetools)
|
||||
|
||||
def _populate_interactivetools_template(self):
|
||||
"""
|
||||
Populate InteractiveTools templated values.
|
||||
"""
|
||||
@@ -571,12 +577,21 @@ class ToolEvaluator:
|
||||
compute environment.
|
||||
"""
|
||||
config_file = self.tool.config_file
|
||||
global_tool_logs(
|
||||
self._create_interactivetools_entry_points, config_file, "Building Interactive Tool Entry Points"
|
||||
)
|
||||
global_tool_logs(self._build_config_files, config_file, "Building Config Files")
|
||||
global_tool_logs(self._build_param_file, config_file, "Building Param File")
|
||||
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
|
||||
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
|
||||
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
|
||||
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
|
||||
return (
|
||||
self.command_line,
|
||||
self.version_command_line,
|
||||
self.extra_filenames,
|
||||
self.environment_variables,
|
||||
self.interactivetools,
|
||||
)
|
||||
|
||||
def _build_command_line(self):
|
||||
"""
|
||||
@@ -819,7 +834,13 @@ class PartialToolEvaluator(ToolEvaluator):
|
||||
def build(self):
|
||||
config_file = self.tool.config_file
|
||||
global_tool_logs(self._build_environment_variables, config_file, "Building Environment Variables")
|
||||
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
|
||||
return (
|
||||
self.command_line,
|
||||
self.version_command_line,
|
||||
self.extra_filenames,
|
||||
self.environment_variables,
|
||||
self.interactivetools,
|
||||
)
|
||||
|
||||
|
||||
class RemoteToolEvaluator(ToolEvaluator):
|
||||
@@ -837,4 +858,10 @@ class RemoteToolEvaluator(ToolEvaluator):
|
||||
global_tool_logs(self._build_param_file, config_file, "Building Param File")
|
||||
global_tool_logs(self._build_command_line, config_file, "Building Command Line")
|
||||
global_tool_logs(self._build_version_command, config_file, "Building Version Command Line")
|
||||
return self.command_line, self.version_command_line, self.extra_filenames, self.environment_variables
|
||||
return (
|
||||
self.command_line,
|
||||
self.version_command_line,
|
||||
self.extra_filenames,
|
||||
self.environment_variables,
|
||||
self.interactivetools,
|
||||
)
|
||||
|
||||
@@ -114,7 +114,7 @@ def main(TMPDIR, WORKING_DIRECTORY, IMPORT_STORE_DIRECTORY):
|
||||
)
|
||||
tool_evaluator.set_compute_environment(compute_environment=SharedComputeEnvironment(job_io=job_io, job=job_io.job))
|
||||
with open(os.path.join(WORKING_DIRECTORY, "tool_script.sh"), "a") as out:
|
||||
command_line, version_command_line, extra_filenames, environment_variables = tool_evaluator.build()
|
||||
command_line, version_command_line, extra_filenames, environment_variables, *_ = tool_evaluator.build()
|
||||
out.write(f'{version_command_line or ""}{command_line}')
|
||||
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ class DatasetCollectionsService(ServiceBase, UsesLibraryMixinItems):
|
||||
raise exceptions.RequestParameterInvalidException(
|
||||
"Parameter instance_type not being 'history' is not yet implemented."
|
||||
)
|
||||
hdca: HistoryDatasetCollectionAssociation = self.collection_manager.get_dataset_collection_instance(
|
||||
hdca: "HistoryDatasetCollectionAssociation" = self.collection_manager.get_dataset_collection_instance(
|
||||
trans, "history", hdca_id
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<container type="docker">galaxy/test-http-example:0.1</container>
|
||||
</requirements>
|
||||
<entry_points>
|
||||
<entry_point name="Simple" requires_domain="True">
|
||||
<entry_point name="Simple IT for $__user_email__" requires_domain="True">
|
||||
<port>7000</port>
|
||||
<url>/</url>
|
||||
</entry_point>
|
||||
|
||||
@@ -113,14 +113,11 @@ class MockEvaluator:
|
||||
self.local_working_directory = local_working_directory
|
||||
self.param_dict = {}
|
||||
|
||||
def populate_interactivetools(self):
|
||||
return []
|
||||
|
||||
def set_compute_environment(self, *args, **kwds):
|
||||
pass
|
||||
|
||||
def build(self):
|
||||
return TEST_COMMAND, "", [], []
|
||||
return TEST_COMMAND, "", [], [], []
|
||||
|
||||
|
||||
class MockJobQueue:
|
||||
|
||||
@@ -139,7 +139,7 @@ class TestToolEvaluator(TestCase, UsesApp):
|
||||
self.tool.config_files.append(("conf1", None, "$thresh"))
|
||||
self.tool._command_line = "prog1 $conf1"
|
||||
self._set_compute_environment()
|
||||
command_line, _, extra_filenames, _ = self.evaluator.build()
|
||||
command_line, _, extra_filenames, *_ = self.evaluator.build()
|
||||
assert len(extra_filenames) == 1
|
||||
config_filename = extra_filenames[0]
|
||||
config_basename = os.path.basename(config_filename)
|
||||
|
||||
Reference in New Issue
Block a user