mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-21 13:50:20 +08:00
Merge pull request #15783 from jmchilton/object_store_ui_followup_2
Convert more of the object store selection client to TypeScript.
This commit is contained in:
@@ -142,6 +142,7 @@
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@cerner/duplicate-package-checker-webpack-plugin": "^2.3.0",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"@vue/test-utils": "^1.3.4",
|
||||
|
||||
@@ -60,4 +60,27 @@ describe("SelectPreferredStore.vue", () => {
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted["updated"][0][0]).toEqual(null);
|
||||
});
|
||||
|
||||
it("updates object store to on non-null selection", async () => {
|
||||
const wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
const els = wrapper.findAll(PREFERENCES.object_store_selection.option_buttons.selector);
|
||||
expect(els.length).toBe(3);
|
||||
const galaxyDefaultOption = wrapper.find(
|
||||
PREFERENCES.object_store_selection.option_button({ object_store_id: "object_store_2" }).selector
|
||||
);
|
||||
expect(galaxyDefaultOption.exists()).toBeTruthy();
|
||||
axiosMock
|
||||
.onPut(
|
||||
`/api/histories/${TEST_HISTORY_ID}`,
|
||||
expect.objectContaining({ preferred_object_store_id: "object_store_2" })
|
||||
)
|
||||
.reply(202);
|
||||
await galaxyDefaultOption.trigger("click");
|
||||
await flushPromises();
|
||||
const errorEl = wrapper.find(".object-store-selection-error");
|
||||
expect(errorEl.exists()).toBeFalsy();
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted["updated"][0][0]).toEqual("object_store_2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,76 +1,69 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import axios from "axios";
|
||||
import SelectObjectStore from "@/components/ObjectStore/SelectObjectStore.vue";
|
||||
import { prependPath } from "@/utils/redirect";
|
||||
import { errorMessageAsString } from "@/utils/simple-error";
|
||||
|
||||
const props = defineProps({
|
||||
userPreferredObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
history: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const error = ref<string | null>(null);
|
||||
const selectedObjectStoreId = ref(props.history.preferred_object_store_id);
|
||||
|
||||
const newDatasetsDescription = "New dataset outputs from tools and workflows executed in this history";
|
||||
const galaxySelectionDefaultTitle = "Use Galaxy Defaults";
|
||||
const galaxySelectionDefaultDescription =
|
||||
"Selecting this will reset Galaxy to default behaviors configured by your Galaxy administrator.";
|
||||
const userSelectionDefaultTitle = "Use Your User Preference Defaults";
|
||||
const userSelectionDefaultDescription =
|
||||
"Selecting this will cause the history to not set a default and to fallback to your user preference defined default.";
|
||||
|
||||
const defaultOptionTitle = computed(() => {
|
||||
if (props.userPreferredObjectStoreId) {
|
||||
return userSelectionDefaultTitle;
|
||||
} else {
|
||||
return galaxySelectionDefaultTitle;
|
||||
}
|
||||
});
|
||||
const defaultOptionDescription = computed(() => {
|
||||
if (props.userPreferredObjectStoreId) {
|
||||
return userSelectionDefaultDescription;
|
||||
} else {
|
||||
return galaxySelectionDefaultDescription;
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "updated", id: string | null): void;
|
||||
}>();
|
||||
|
||||
async function handleSubmit(preferredObjectStoreId: string | null) {
|
||||
const payload = { preferred_object_store_id: preferredObjectStoreId };
|
||||
const url = prependPath(`api/histories/${props.history.id}`);
|
||||
try {
|
||||
await axios.put(url, payload);
|
||||
} catch (e) {
|
||||
error.value = errorMessageAsString(e);
|
||||
}
|
||||
selectedObjectStoreId.value = preferredObjectStoreId;
|
||||
emit("updated", preferredObjectStoreId);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<SelectObjectStore
|
||||
:parent-error="error"
|
||||
:parent-error="error || undefined"
|
||||
:for-what="newDatasetsDescription"
|
||||
:selected-object-store-id="selectedObjectStoreId"
|
||||
:default-option-title="defaultOptionTitle"
|
||||
:default-option-description="defaultOptionDescription"
|
||||
@onSubmit="handleSubmit" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import SelectObjectStore from "components/ObjectStore/SelectObjectStore";
|
||||
import { prependPath } from "utils/redirect";
|
||||
import { errorMessageAsString } from "utils/simple-error";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectObjectStore,
|
||||
},
|
||||
props: {
|
||||
userPreferredObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
history: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const selectedObjectStoreId = this.history.preferred_object_store_id;
|
||||
return {
|
||||
error: null,
|
||||
selectedObjectStoreId: selectedObjectStoreId,
|
||||
newDatasetsDescription: "New dataset outputs from tools and workflows executed in this history",
|
||||
popoverPlacement: "left",
|
||||
galaxySelectionDefaultTitle: "Use Galaxy Defaults",
|
||||
galaxySelectionDefaultDescription:
|
||||
"Selecting this will reset Galaxy to default behaviors configured by your Galaxy administrator.",
|
||||
userSelectionDefaultTitle: "Use Your User Preference Defaults",
|
||||
userSelectionDefaultDescription:
|
||||
"Selecting this will cause the history to not set a default and to fallback to your user preference defined default.",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultOptionTitle() {
|
||||
if (this.userPreferredObjectStoreId) {
|
||||
return this.userSelectionDefaultTitle;
|
||||
} else {
|
||||
return this.galaxySelectionDefaultTitle;
|
||||
}
|
||||
},
|
||||
defaultOptionDescription() {
|
||||
if (this.userPreferredObjectStoreId) {
|
||||
return this.userSelectionDefaultDescription;
|
||||
} else {
|
||||
return this.galaxySelectionDefaultDescription;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async handleSubmit(preferredObjectStoreId) {
|
||||
const payload = { preferred_object_store_id: preferredObjectStoreId };
|
||||
const url = prependPath(`api/histories/${this.history.id}`);
|
||||
try {
|
||||
await axios.put(url, payload);
|
||||
} catch (e) {
|
||||
this.error = errorMessageAsString(e);
|
||||
}
|
||||
this.selectedObjectStoreId = preferredObjectStoreId;
|
||||
this.$emit("updated", preferredObjectStoreId);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -32,7 +32,7 @@ import ObjectStoreRestrictionSpan from "./ObjectStoreRestrictionSpan";
|
||||
import QuotaUsageBar from "components/User/DiskUsage/Quota/QuotaUsageBar";
|
||||
import { QuotaSourceUsageProvider } from "components/User/DiskUsage/Quota/QuotaUsageProvider";
|
||||
import ObjectStoreBadges from "./ObjectStoreBadges";
|
||||
import adminConfigMixin from "./adminConfigMixin";
|
||||
import { adminMarkup } from "./adminConfig";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -41,7 +41,6 @@ export default {
|
||||
QuotaSourceUsageProvider,
|
||||
QuotaUsageBar,
|
||||
},
|
||||
mixins: [adminConfigMixin],
|
||||
props: {
|
||||
storageInfo: {
|
||||
type: Object,
|
||||
@@ -57,7 +56,7 @@ export default {
|
||||
return this.storageInfo.quota?.source;
|
||||
},
|
||||
descriptionRendered() {
|
||||
return this.adminMarkup(this.storageInfo.description);
|
||||
return adminMarkup(this.storageInfo.description);
|
||||
},
|
||||
isPrivate() {
|
||||
return this.storageInfo.private;
|
||||
|
||||
@@ -23,8 +23,10 @@ describe("ObjectStoreBadge", () => {
|
||||
const selector = ROOT_COMPONENT.object_store_details.badge_of_type({ type: "more_secure" }).selector;
|
||||
const iconEl = wrapper.find(selector);
|
||||
expect(iconEl.exists()).toBeTruthy();
|
||||
expect(wrapper.vm.message).toContain(TEST_MESSAGE);
|
||||
expect(wrapper.vm.stockMessage).toContain("more secure by the Galaxy adminstrator");
|
||||
const popoverStub = wrapper.find("b-popover-stub");
|
||||
const popoverText = popoverStub.text();
|
||||
expect(popoverText).toContain(TEST_MESSAGE);
|
||||
expect(popoverText).toContain("more secure by the Galaxy adminstrator");
|
||||
});
|
||||
|
||||
it("should render a valid badge for less_secure type", async () => {
|
||||
@@ -32,7 +34,9 @@ describe("ObjectStoreBadge", () => {
|
||||
const selector = ROOT_COMPONENT.object_store_details.badge_of_type({ type: "less_secure" }).selector;
|
||||
const iconEl = wrapper.find(selector);
|
||||
expect(iconEl.exists()).toBeTruthy();
|
||||
expect(wrapper.vm.message).toContain(TEST_MESSAGE);
|
||||
expect(wrapper.vm.stockMessage).toContain("less secure by the Galaxy adminstrator");
|
||||
const popoverStub = wrapper.find("b-popover-stub");
|
||||
const popoverText = popoverStub.text();
|
||||
expect(popoverText).toContain(TEST_MESSAGE);
|
||||
expect(popoverText).toContain("less secure by the Galaxy adminstrator");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,79 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { adminMarkup } from "./adminConfig";
|
||||
import { FontAwesomeIcon, FontAwesomeLayers } from "@fortawesome/vue-fontawesome";
|
||||
import type { components } from "@/schema";
|
||||
import "./badgeIcons";
|
||||
|
||||
type BadgeType = components["schemas"]["BadgeDict"];
|
||||
|
||||
const MESSAGES = {
|
||||
restricted:
|
||||
"This dataset is stored on storage restricted to a single user. It can not be shared, pubished, or added to Galaxy data libraries.",
|
||||
user_defined: "This storage was user defined and is not managed by the Galaxy adminstrator.",
|
||||
quota: "A Galaxy quota is enabled for this object store.",
|
||||
no_quota: "No Galaxy quota is enabled for this object store.",
|
||||
faster: "This storage has been marked as a faster option by the Galaxy adminstrator.",
|
||||
slower: "This storage has been marked as a slower option by the Galaxy adminstrator.",
|
||||
short_term: "This storage has been marked routinely purged by the Galaxy adminstrator.",
|
||||
backed_up: "This storage has been marked as backed up by the Galaxy adminstrator.",
|
||||
not_backed_up: "This storage has been marked as not backed up by the Galaxy adminstrator.",
|
||||
more_secure:
|
||||
"This storage has been marked as more secure by the Galaxy adminstrator. The Galaxy web application doesn't make any additional promises regarding security for this storage.",
|
||||
less_secure:
|
||||
"This storage has been marked as less secure by the Galaxy adminstrator. The Galaxy web application doesn't make any additional promises regarding security for this storage.",
|
||||
more_stable:
|
||||
"This storage has been marked as more stable by the Galaxy adminstrator - expect jobs to fail less because of storage issues for this storage.",
|
||||
less_stable:
|
||||
"This storage has been marked as less stable by the Galaxy adminstrator - expect jobs to fail more because of storage issues for this storage.",
|
||||
cloud: "This is cloud storage.",
|
||||
};
|
||||
|
||||
interface ObjectStoreBadgeProps {
|
||||
badge: BadgeType;
|
||||
size?: string;
|
||||
moreOnHover?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ObjectStoreBadgeProps>(), {
|
||||
size: "3x",
|
||||
moreOnHover: true,
|
||||
});
|
||||
|
||||
const advantage = "storage-advantage";
|
||||
const disadvantage = "storage-disadvantage";
|
||||
const neutral = "storage-neutral";
|
||||
const transparent = "reduced-opacity";
|
||||
|
||||
const stockMessage = computed(() => {
|
||||
return MESSAGES[props.badge.type];
|
||||
});
|
||||
|
||||
const layerClasses = computed(() => {
|
||||
return [`fa-${props.size}`, "fa-fw"];
|
||||
});
|
||||
|
||||
const badgeType = computed(() => {
|
||||
return props.badge.type;
|
||||
});
|
||||
|
||||
const shrink = computed(() => {
|
||||
return { transform: "shrink-6" };
|
||||
});
|
||||
|
||||
const message = computed(() => {
|
||||
return adminMarkup(props.badge.message);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>
|
||||
<span ref="iconTarget" class="object-store-badge-wrapper">
|
||||
<FontAwesomeLayers :class="layerClasses" :data-badge-type="badgeType">
|
||||
<FontAwesomeIcon v-if="badgeType == 'restricted'" icon="user-lock" :class="disadvantage" />
|
||||
<!--
|
||||
<FontAwesomeIcon v-if="badgeType == 'user_defined'" icon="plug" :class="neutral" />
|
||||
-->
|
||||
<FontAwesomeIcon v-if="badgeType == 'quota'" icon="chart-line" :class="disadvantage" />
|
||||
<FontAwesomeIcon v-if="badgeType == 'no_quota'" icon="chart-line" :class="neutral" v-bind="shrink" />
|
||||
<FontAwesomeIcon v-if="badgeType == 'no_quota'" icon="ban" :class="[transparent, advantage]" />
|
||||
@@ -70,108 +140,6 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import adminConfigMixin from "./adminConfigMixin";
|
||||
import { FontAwesomeIcon, FontAwesomeLayers } from "@fortawesome/vue-fontawesome";
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
faUserLock,
|
||||
faChartLine,
|
||||
faBan,
|
||||
faCircleNotch,
|
||||
faPlug,
|
||||
faTachometerAlt,
|
||||
faArchive,
|
||||
faRecycle,
|
||||
faKey,
|
||||
faShieldAlt,
|
||||
faCloud,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
faUserLock,
|
||||
faChartLine,
|
||||
faBan,
|
||||
faCircleNotch,
|
||||
faPlug,
|
||||
faTachometerAlt,
|
||||
faArchive,
|
||||
faRecycle,
|
||||
faKey,
|
||||
faShieldAlt,
|
||||
faCloud
|
||||
);
|
||||
|
||||
const MESSAGES = {
|
||||
restricted:
|
||||
"This dataset is stored on storage restricted to a single user. It can not be shared, pubished, or added to Galaxy data libraries.",
|
||||
user_defined: "This storage was user defined and is not managed by the Galaxy adminstrator.",
|
||||
quota: "A Galaxy quota is enabled for this object store.",
|
||||
no_quota: "No Galaxy quota is enabled for this object store.",
|
||||
faster: "This storage has been marked as a faster option by the Galaxy adminstrator.",
|
||||
slower: "This storage has been marked as a slower option by the Galaxy adminstrator.",
|
||||
short_term: "This storage has been marked routinely purged by the Galaxy adminstrator.",
|
||||
backed_up: "This storage has been marked as backed up by the Galaxy adminstrator.",
|
||||
not_backed_up: "This storage has been marked as not backed up by the Galaxy adminstrator.",
|
||||
more_secure:
|
||||
"This storage has been marked as more secure by the Galaxy adminstrator. The Galaxy web application doesn't make any additional promises regarding security for this storage.",
|
||||
less_secure:
|
||||
"This storage has been marked as less secure by the Galaxy adminstrator. The Galaxy web application doesn't make any additional promises regarding security for this storage.",
|
||||
more_stable:
|
||||
"This storage has been marked as more stable by the Galaxy adminstrator - expect jobs to fail less because of storage issues for this storage.",
|
||||
less_stable:
|
||||
"This storage has been marked as less stable by the Galaxy adminstrator - expect jobs to fail more because of storage issues for this storage.",
|
||||
cloud: "This is cloud storage.",
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FontAwesomeLayers,
|
||||
FontAwesomeIcon,
|
||||
},
|
||||
mixins: [adminConfigMixin],
|
||||
props: {
|
||||
badge: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: "3x",
|
||||
},
|
||||
moreOnHover: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
advantage: "storage-advantage",
|
||||
disadvantage: "storage-disadvantage",
|
||||
neutral: "storage-neutral",
|
||||
transparent: "reduced-opacity",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
stockMessage() {
|
||||
return MESSAGES[this.badge.type];
|
||||
},
|
||||
layerClasses() {
|
||||
return [`fa-${this.size}`, "fa-fw"];
|
||||
},
|
||||
badgeType() {
|
||||
return this.badge.type;
|
||||
},
|
||||
shrink() {
|
||||
return { transform: "shrink-6" };
|
||||
},
|
||||
message() {
|
||||
return this.adminMarkup(this.badge.message);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.reduced-opacity {
|
||||
opacity: 0.65;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "tests/jest/helpers";
|
||||
import ObjectStoreBadges from "./ObjectStoreBadges";
|
||||
import ObjectStoreBadge from "./ObjectStoreBadge";
|
||||
|
||||
const localVue = getLocalVue(true);
|
||||
|
||||
const TEST_MESSAGE = "a test message provided by backend";
|
||||
const BADGES = [
|
||||
{ type: "more_secure", message: TEST_MESSAGE },
|
||||
{ type: "slower", message: TEST_MESSAGE },
|
||||
];
|
||||
|
||||
describe("ObjectStoreBadges", () => {
|
||||
let wrapper;
|
||||
|
||||
it("should render all badges in array", async () => {
|
||||
wrapper = shallowMount(ObjectStoreBadges, {
|
||||
propsData: { badges: BADGES },
|
||||
localVue,
|
||||
});
|
||||
const badgeListEl = wrapper.find(".object-store-badges");
|
||||
expect(badgeListEl.exists()).toBeTruthy();
|
||||
const badges = wrapper.findAllComponents(ObjectStoreBadge);
|
||||
expect(badges.length).toBe(2);
|
||||
expect(badges.at(0).attributes("size")).toBe("3x");
|
||||
});
|
||||
|
||||
it("should pass along size attributes", async () => {
|
||||
wrapper = shallowMount(ObjectStoreBadges, {
|
||||
propsData: { badges: BADGES, size: "2x" },
|
||||
localVue,
|
||||
});
|
||||
const badgeListEl = wrapper.find(".object-store-badges");
|
||||
expect(badgeListEl.exists()).toBeTruthy();
|
||||
const badges = wrapper.findAllComponents(ObjectStoreBadge);
|
||||
expect(badges.length).toBe(2);
|
||||
expect(badges.at(0).attributes("size")).toBe("2x");
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import ObjectStoreBadge from "./ObjectStoreBadge.vue";
|
||||
import type { components } from "@/schema";
|
||||
type BadgeType = components["schemas"]["BadgeDict"];
|
||||
|
||||
interface ObjectStoreBadgesProps {
|
||||
badges: Array<BadgeType>;
|
||||
size?: string;
|
||||
moreOnHover?: boolean;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<ObjectStoreBadgesProps>(), {
|
||||
size: "3x",
|
||||
moreOnHover: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="object-store-badges">
|
||||
<ObjectStoreBadge
|
||||
@@ -5,31 +22,7 @@
|
||||
:key="idx"
|
||||
:badge="badge"
|
||||
:size="size"
|
||||
:moreOnHover="moreOnHover">
|
||||
:more-on-hover="moreOnHover">
|
||||
</ObjectStoreBadge>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectStoreBadge from "./ObjectStoreBadge";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ObjectStoreBadge,
|
||||
},
|
||||
props: {
|
||||
badges: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: "3x",
|
||||
},
|
||||
moreOnHover: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import Vue from "vue";
|
||||
import BootstrapVue from "bootstrap-vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
Vue.use(BootstrapVue);
|
||||
|
||||
const props = defineProps({
|
||||
isPrivate: Boolean,
|
||||
});
|
||||
|
||||
const text = computed(() => (props.isPrivate ? "private" : "unrestricted"));
|
||||
const title = computed(() => {
|
||||
if (props.isPrivate) {
|
||||
return "This dataset is stored on storage restricted to a single user. It can not be shared, published, or added to Galaxy data libraries.";
|
||||
} else {
|
||||
return "This dataset is stored on unrestricted storage. With sufficient Galaxy permissions, this dataset can be published, shared, or added to Galaxy data libraries.";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-b-tooltip.hover class="stored-how" :title="title">{{ text }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import BootstrapVue from "bootstrap-vue";
|
||||
Vue.use(BootstrapVue);
|
||||
|
||||
export default {
|
||||
props: {
|
||||
isPrivate: {
|
||||
// private is reserved word
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
text() {
|
||||
return this.isPrivate ? "private" : "unrestricted";
|
||||
},
|
||||
title() {
|
||||
if (this.isPrivate) {
|
||||
return "This dataset is stored on storage restricted to a single user. It can not be shared, pubished, or added to Galaxy data libraries.";
|
||||
} else {
|
||||
return "This dataset is stored on unrestricted storage. With sufficient Galaxy permissions, this dataset can be published, shared, or added to Galaxy data libraries.";
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Give visual indication of mouseover info */
|
||||
.stored-how {
|
||||
|
||||
@@ -7,29 +7,17 @@ import ObjectStoreBadges from "@/components/ObjectStore/ObjectStoreBadges.vue";
|
||||
import ProvidedQuotaSourceUsageBar from "@/components/User/DiskUsage/Quota/ProvidedQuotaSourceUsageBar.vue";
|
||||
import { getSelectableObjectStores } from "./services";
|
||||
|
||||
const props = defineProps({
|
||||
selectedObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
defaultOptionTitle: {
|
||||
// "Use Your User Preference Defaults"
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
defaultOptionDescription: {
|
||||
// "Selecting this will cause the history to not set a default and to fallback to your user preference defined default."
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
forWhat: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
parentError: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
interface SelectObjectStoreProps {
|
||||
selectedObjectStoreId?: String | null;
|
||||
defaultOptionTitle: String;
|
||||
defaultOptionDescription: String;
|
||||
forWhat: String;
|
||||
parentError?: String | null;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<SelectObjectStoreProps>(), {
|
||||
selectedObjectStoreId: null,
|
||||
parentError: null,
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
@@ -77,7 +65,9 @@ function variant(objectStoreId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(["onSubmit"]);
|
||||
const emit = defineEmits<{
|
||||
(e: "onSubmit", id: string | null): void;
|
||||
}>();
|
||||
|
||||
async function handleSubmit(preferredObjectStoreId: string) {
|
||||
emit("onSubmit", preferredObjectStoreId);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "tests/jest/helpers";
|
||||
import ShowSelectedObjectStore from "./ShowSelectedObjectStore";
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
import DescribeObjectStore from "@/components/ObjectStore/DescribeObjectStore.vue";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import flushPromises from "flush-promises";
|
||||
@@ -34,12 +36,12 @@ describe("ShowSelectedObjectStore", () => {
|
||||
DescribeObjectStore: true,
|
||||
},
|
||||
});
|
||||
let loadingEl = wrapper.find("loadingspan-stub");
|
||||
let loadingEl = wrapper.findComponent(LoadingSpan);
|
||||
expect(loadingEl.exists()).toBeTruthy();
|
||||
expect(loadingEl.attributes("message")).toBeLocalizationOf("Loading object store details");
|
||||
expect(loadingEl.find(".loading-message").text()).toContainLocalizationOf("Loading object store details");
|
||||
await flushPromises();
|
||||
loadingEl = wrapper.find("loadingspan-stub");
|
||||
loadingEl = wrapper.findComponent(LoadingSpan);
|
||||
expect(loadingEl.exists()).toBeFalsy();
|
||||
expect(wrapper.find("describeobjectstore-stub").exists()).toBeTruthy();
|
||||
expect(wrapper.findComponent(DescribeObjectStore).exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
import { ObjectStoreDetailsProvider } from "@/components/providers/ObjectStoreProvider";
|
||||
import DescribeObjectStore from "@/components/ObjectStore/DescribeObjectStore.vue";
|
||||
|
||||
interface ShowSelectObjectStoreProps {
|
||||
forWhat: String;
|
||||
preferredObjectStoreId?: String | null;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<ShowSelectObjectStoreProps>(), {
|
||||
preferredObjectStoreId: null,
|
||||
});
|
||||
|
||||
const loadingMessage = "Loading object store details";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ObjectStoreDetailsProvider
|
||||
:id="preferredObjectStoreId"
|
||||
@@ -6,32 +23,3 @@
|
||||
<DescribeObjectStore v-else :what="forWhat" :storage-info="storageInfo"> </DescribeObjectStore>
|
||||
</ObjectStoreDetailsProvider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LoadingSpan from "components/LoadingSpan";
|
||||
import { ObjectStoreDetailsProvider } from "components/providers/ObjectStoreProvider";
|
||||
import DescribeObjectStore from "components/ObjectStore/DescribeObjectStore";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DescribeObjectStore,
|
||||
LoadingSpan,
|
||||
ObjectStoreDetailsProvider,
|
||||
},
|
||||
props: {
|
||||
forWhat: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
preferredObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadingMessage: "Loading object store details",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import MarkdownIt from "markdown-it";
|
||||
|
||||
export function adminMarkup(markup: string): string | null {
|
||||
let markupHtml;
|
||||
if (markup) {
|
||||
markupHtml = MarkdownIt({ html: true }).render(markup);
|
||||
} else {
|
||||
markupHtml = null;
|
||||
}
|
||||
return markupHtml;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import MarkdownIt from "markdown-it";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
adminMarkup(markup) {
|
||||
let markupHtml;
|
||||
if (markup) {
|
||||
markupHtml = MarkdownIt({ html: true }).render(markup);
|
||||
} else {
|
||||
markupHtml = null;
|
||||
}
|
||||
return markupHtml;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/* I can't get this to type properly in type script, so
|
||||
handling it here in JavaScript. I get a variant of this
|
||||
error (https://github.com/FortAwesome/Font-Awesome/issues/12575).
|
||||
*/
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
import {
|
||||
faUserLock,
|
||||
faChartLine,
|
||||
faBan,
|
||||
faCircleNotch,
|
||||
faPlug,
|
||||
faTachometerAlt,
|
||||
faArchive,
|
||||
faRecycle,
|
||||
faKey,
|
||||
faShieldAlt,
|
||||
faCloud,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
faUserLock,
|
||||
faChartLine,
|
||||
faBan,
|
||||
faCircleNotch,
|
||||
faPlug,
|
||||
faTachometerAlt,
|
||||
faArchive,
|
||||
faRecycle,
|
||||
faKey,
|
||||
faShieldAlt,
|
||||
faCloud
|
||||
);
|
||||
@@ -0,0 +1,41 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "tests/jest/helpers";
|
||||
import { setupSelectableMock } from "@/components/ObjectStore/mockServices";
|
||||
setupSelectableMock();
|
||||
|
||||
import flushPromises from "flush-promises";
|
||||
|
||||
import ToolSelectPreferredObjectStore from "./ToolSelectPreferredObjectStore.vue";
|
||||
|
||||
const localVue = getLocalVue(true);
|
||||
|
||||
function mountComponent() {
|
||||
const wrapper = mount(ToolSelectPreferredObjectStore, {
|
||||
propsData: { toolPreferredObjectStoreId: null },
|
||||
localVue,
|
||||
});
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
import { ROOT_COMPONENT } from "@/utils/navigation";
|
||||
|
||||
const PREFERENCES = ROOT_COMPONENT.preferences;
|
||||
|
||||
describe("ToolSelectPreferredObjectStore.vue", () => {
|
||||
it("updates object store to default on selection null", async () => {
|
||||
const wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
const els = wrapper.findAll(PREFERENCES.object_store_selection.option_buttons.selector);
|
||||
expect(els.length).toBe(3);
|
||||
const galaxyDefaultOption = wrapper.find(
|
||||
PREFERENCES.object_store_selection.option_button({ object_store_id: "__null__" }).selector
|
||||
);
|
||||
expect(galaxyDefaultOption.exists()).toBeTruthy();
|
||||
await galaxyDefaultOption.trigger("click");
|
||||
await flushPromises();
|
||||
const errorEl = wrapper.find(".object-store-selection-error");
|
||||
expect(errorEl.exists()).toBeFalsy();
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted["updated"][0][0]).toEqual(null);
|
||||
});
|
||||
});
|
||||
@@ -1,48 +1,36 @@
|
||||
<script lang="ts" setup>
|
||||
import SelectObjectStore from "@/components/ObjectStore/SelectObjectStore.vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface ToolSelectProps {
|
||||
toolPreferredObjectStoreId?: String | null;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ToolSelectProps>(), {
|
||||
toolPreferredObjectStoreId: null,
|
||||
});
|
||||
|
||||
const selectedObjectStoreId = ref<String | null>(props.toolPreferredObjectStoreId);
|
||||
const newDatasetsDescription = "The default object store for the outputs of this tool";
|
||||
const defaultOptionTitle = "Use Defaults";
|
||||
const defaultOptionDescription =
|
||||
"If the history has a default set, that will be used. If instead, you've set an option in your user preferences - that will be assumed to be your default selection. Finally, the Galaxy configuration will be used.";
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "updated", id: string | null): void;
|
||||
}>();
|
||||
|
||||
async function handleSubmit(preferredObjectStoreId: string | null) {
|
||||
selectedObjectStoreId.value = preferredObjectStoreId;
|
||||
emit("updated", preferredObjectStoreId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectObjectStore
|
||||
:root="root"
|
||||
:for-what="newDatasetsDescription"
|
||||
:selected-object-store-id="selectedObjectStoreId"
|
||||
:default-option-title="defaultOptionTitle"
|
||||
:default-option-description="defaultOptionDescription"
|
||||
@onSubmit="handleSubmit" />
|
||||
</template>
|
||||
<script>
|
||||
import SelectObjectStore from "components/ObjectStore/SelectObjectStore";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectObjectStore,
|
||||
},
|
||||
props: {
|
||||
root: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
toolPreferredObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedObjectStoreId: this.toolPreferredObjectStoreId,
|
||||
newDatasetsDescription: "The default object store for the outputs of this tool",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultOptionTitle() {
|
||||
return "Use Defaults";
|
||||
},
|
||||
defaultOptionDescription() {
|
||||
return "If the history has a default set, that will be used. If instead, you've set an option in your user preferences - that will be assumed to be your default selection. Finally, the Galaxy configuration will be used.";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async handleSubmit(preferredObjectStoreId) {
|
||||
this.selectedObjectStoreId = preferredObjectStoreId;
|
||||
this.$emit("updated", preferredObjectStoreId);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<ShowSelectedObjectStore
|
||||
v-if="toolPreferredObjectStoreId"
|
||||
:preferred-object-store-id="toolPreferredObjectStoreId"
|
||||
forWhat="Galaxy will default to storing this tool run's output in">
|
||||
for-what="Galaxy will default to storing this tool run's output in">
|
||||
</ShowSelectedObjectStore>
|
||||
<div v-else>
|
||||
No selection has been made for this tool execution. Defaults from history, user, or Galaxy will be used.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "tests/jest/helpers";
|
||||
import { setupSelectableMock } from "@/components/ObjectStore/mockServices";
|
||||
setupSelectableMock();
|
||||
|
||||
import flushPromises from "flush-promises";
|
||||
|
||||
import WorkflowSelectPreferredObjectStore from "./WorkflowSelectPreferredObjectStore.vue";
|
||||
|
||||
const localVue = getLocalVue(true);
|
||||
|
||||
function mountComponent() {
|
||||
const wrapper = mount(WorkflowSelectPreferredObjectStore, {
|
||||
propsData: { invocationPreferredObjectStoreId: null },
|
||||
localVue,
|
||||
});
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
import { ROOT_COMPONENT } from "@/utils/navigation";
|
||||
|
||||
const PREFERENCES = ROOT_COMPONENT.preferences;
|
||||
|
||||
describe("WorkflowSelectPreferredObjectStore.vue", () => {
|
||||
it("updates object store to default on selection null", async () => {
|
||||
const wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
const els = wrapper.findAll(PREFERENCES.object_store_selection.option_buttons.selector);
|
||||
expect(els.length).toBe(3);
|
||||
const galaxyDefaultOption = wrapper.find(
|
||||
PREFERENCES.object_store_selection.option_button({ object_store_id: "__null__" }).selector
|
||||
);
|
||||
expect(galaxyDefaultOption.exists()).toBeTruthy();
|
||||
await galaxyDefaultOption.trigger("click");
|
||||
await flushPromises();
|
||||
const errorEl = wrapper.find(".object-store-selection-error");
|
||||
expect(errorEl.exists()).toBeFalsy();
|
||||
const emitted = wrapper.emitted();
|
||||
expect(emitted["updated"][0][0]).toEqual(null);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import SelectObjectStore from "@/components/ObjectStore/SelectObjectStore.vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface Props {
|
||||
invocationPreferredObjectStoreId?: String | null;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
invocationPreferredObjectStoreId: null,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "updated", id: string | null): void;
|
||||
}>();
|
||||
|
||||
const selectedObjectStoreId = ref<String | null>(props.invocationPreferredObjectStoreId);
|
||||
const newDatasetsDescription = "The default object store for the outputs of this workflow invocation";
|
||||
const defaultOptionTitle = "Use Defaults";
|
||||
const defaultOptionDescription =
|
||||
"If the history has a default set, that will be used. If instead, you've set an option in your user preferences - that will be assumed to be your default selection. Finally, the Galaxy configuration will be used.";
|
||||
|
||||
async function handleSubmit(preferredObjectStoreId: string | null) {
|
||||
selectedObjectStoreId.value = preferredObjectStoreId;
|
||||
emit("updated", preferredObjectStoreId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectObjectStore
|
||||
:for-what="newDatasetsDescription"
|
||||
@@ -6,38 +34,3 @@
|
||||
:default-option-description="defaultOptionDescription"
|
||||
@onSubmit="handleSubmit" />
|
||||
</template>
|
||||
<script>
|
||||
import SelectObjectStore from "components/ObjectStore/SelectObjectStore";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectObjectStore,
|
||||
},
|
||||
props: {
|
||||
invocationPreferredObjectStoreId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedObjectStoreId: this.invocationPreferredObjectStoreId,
|
||||
newDatasetsDescription: "The default object store for the outputs of this workflow invocation",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultOptionTitle() {
|
||||
return "Use Defaults";
|
||||
},
|
||||
defaultOptionDescription() {
|
||||
return "If the history has a default set, that will be used. If instead, you've set an option in your user preferences - that will be assumed to be your default selection. Finally, the Galaxy configuration will be used.";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async handleSubmit(preferredObjectStoreId) {
|
||||
this.selectedObjectStoreId = preferredObjectStoreId;
|
||||
this.$emit("updated", preferredObjectStoreId);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</b-button>
|
||||
<WorkflowTargetPreferredObjectStorePopover
|
||||
target="workflow-storage-indicator-primary"
|
||||
:title-suffix="suffixPrimary"
|
||||
:invocation-preferred-object-store-id="selectedObjectStoreId">
|
||||
</WorkflowTargetPreferredObjectStorePopover>
|
||||
<b-modal
|
||||
|
||||
@@ -72,6 +72,20 @@ expect.extend({
|
||||
};
|
||||
}
|
||||
},
|
||||
toContainLocalizationOf(received, str) {
|
||||
const pass = received.indexOf(testLocalize(str)) >= 0;
|
||||
if (pass) {
|
||||
return {
|
||||
message: () => `expected ${received} to contain localization of ${str}`,
|
||||
pass: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
message: () => `expected ${received} to contain localization of ${str}`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Creates a watcher on the indicated vm/prop for use in testing
|
||||
|
||||
@@ -2348,6 +2348,24 @@
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
|
||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||
|
||||
"@types/linkify-it@*":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9"
|
||||
integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==
|
||||
|
||||
"@types/markdown-it@^12.2.3":
|
||||
version "12.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51"
|
||||
integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==
|
||||
dependencies:
|
||||
"@types/linkify-it" "*"
|
||||
"@types/mdurl" "*"
|
||||
|
||||
"@types/mdurl@*":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
||||
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
||||
|
||||
"@types/mime@*":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user