Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Object Store Follow Up #15693

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,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",
Expand Down
131 changes: 63 additions & 68 deletions client/src/components/History/CurrentHistory/SelectPreferredStore.vue
Original file line number Diff line number Diff line change
@@ -1,76 +1,71 @@
<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 = ref("New dataset outputs from tools and workflows executed in this history");
const galaxySelectionDefaultTitle = ref("Use Galaxy Defaults");
const galaxySelectionDefaultDescription = ref(
"Selecting this will reset Galaxy to default behaviors configured by your Galaxy administrator."
);
const userSelectionDefaultTitle = ref("Use Your User Preference Defaults");
const userSelectionDefaultDescription = ref(
"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.value;
} else {
return galaxySelectionDefaultTitle.value;
}
});
const defaultOptionDescription = computed(() => {
if (props.userPreferredObjectStoreId) {
return userSelectionDefaultDescription.value;
} else {
return galaxySelectionDefaultDescription.value;
}
});

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>
5 changes: 2 additions & 3 deletions client/src/components/ObjectStore/DescribeObjectStore.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -41,7 +41,6 @@ export default {
QuotaSourceUsageProvider,
QuotaUsageBar,
},
mixins: [adminConfigMixin],
props: {
storageInfo: {
type: Object,
Expand All @@ -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;
Expand Down
12 changes: 8 additions & 4 deletions client/src/components/ObjectStore/ObjectStoreBadge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ 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 () => {
mountBadge({ type: "less_secure", message: TEST_MESSAGE });
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");
});
});
172 changes: 70 additions & 102 deletions client/src/components/ObjectStore/ObjectStoreBadge.vue
Original file line number Diff line number Diff line change
@@ -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]" />
Expand Down Expand Up @@ -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;
Expand Down
Loading