Skip to content

Commit

Permalink
UI for "relocating" a dataset's object store.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Feb 7, 2024
1 parent 6c75034 commit 3d33173
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 1 deletion.
5 changes: 5 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3822,6 +3822,11 @@ export interface components {
percent_used: number | null;
/** @description Information about quota sources around dataset storage. */
quota: components["schemas"]["ConcreteObjectStoreQuotaSourceDetails"];
/**
* Relocatable
* @description Indicator of whether the objectstore for this dataset can be switched by this user.
*/
relocatable: boolean;
/**
* Shareable
* @description Is this dataset shareable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DatasetStorageDetails } from "@/api";
import { fetchDatasetStorage } from "@/api/datasets";
import { errorMessageAsString } from "@/utils/simple-error";
import RelocateLink from "./RelocateLink.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
import DescribeObjectStore from "@/components/ObjectStore/DescribeObjectStore.vue";
Expand Down Expand Up @@ -60,7 +61,10 @@ watch(

<template>
<div>
<h2 v-if="includeTitle" class="h-md">Dataset Storage</h2>
<h2 v-if="includeTitle" class="h-md">
Dataset Storage
<RelocateLink v-if="storageInfo" :dataset-storage-details="storageInfo" />
</h2>
<div v-if="errorMessage" class="error">{{ errorMessage }}</div>
<LoadingSpan v-else-if="storageInfo == null"> </LoadingSpan>
<div v-else-if="discarded">
Expand Down
50 changes: 50 additions & 0 deletions client/src/components/Dataset/DatasetStorage/RelocateDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { ConcreteObjectStoreModel } from "@/api";

Check failure on line 3 in client/src/components/Dataset/DatasetStorage/RelocateDialog.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!
import ObjectStoreBadges from "@/components/ObjectStore/ObjectStoreBadges.vue";
import ProvidedQuotaSourceUsageBar from "@/components/User/DiskUsage/Quota/ProvidedQuotaSourceUsageBar.vue";
import RelocateDialog from "./RelocateDialog.vue";

Check failure on line 7 in client/src/components/Dataset/DatasetStorage/RelocateDialog.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

'RelocateDialog' is defined but never used. Allowed unused vars must match /_.+/u
interface RelocateProps {
fromObjectStore: ConcreteObjectStoreModel;
targetObjectStores: ConcreteObjectStoreModel[];
}
defineProps<RelocateProps>();
</script>

<template>
<div>
<p>Relocate the target object store:</p>
<b-button-group vertical size="lg" style="width: 100%">
<b-button
:id="`swap-target-object-store-button-${fromObjectStore.object_store_id}`"
:key="fromObjectStore.object_store_id"
class="swap-target-object-store-select-button"
variant="outline-primary"
:data-object-store-id="fromObjectStore.object_store_id"
>{{ fromObjectStore.name }}
<ObjectStoreBadges :badges="fromObjectStore.badges" size="lg" :more-on-hover="false" />
<ProvidedQuotaSourceUsageBar :object-store="object_store" :compact="true">
</ProvidedQuotaSourceUsageBar>
</b-button>
</b-button-group>
<p>Select a new target from:</p>
<b-button-group vertical size="lg" style="width: 100%">
<b-button
v-for="object_store in targetObjectStores"
:id="`swap-target-object-store-button-${object_store.object_store_id}`"
:key="object_store.object_store_id"
class="swap-target-object-store-select-button"
:data-object-store-id="object_store.object_store_id"
variant="outline-info"
@click="handleSubmit(object_store.object_store_id)"
>{{ object_store.name }}
<ObjectStoreBadges :badges="object_store.badges" size="lg" :more-on-hover="false" />
<ProvidedQuotaSourceUsageBar :object-store="object_store" :compact="true">
</ProvidedQuotaSourceUsageBar>
</b-button>
</b-button-group>
</div>
</template>
72 changes: 72 additions & 0 deletions client/src/components/Dataset/DatasetStorage/RelocateLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script setup lang="ts">
import { storeToRefs } from "pinia";

Check failure on line 2 in client/src/components/Dataset/DatasetStorage/RelocateLink.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!
import { computed, ref } from "vue";
import { ConcreteObjectStoreModel, DatasetStorageDetails } from "@/api";
import RelocateModal from "./RelocateModal.vue";
import { useObjectStoreStore } from "@/stores/objectStoreStore";
interface RelocateLinkProps {
datasetStorageDetails: DatasetStorageDetails;
}
const props = defineProps<RelocateLinkProps>();
const relocateModal = ref<InstanceType<typeof RelocateModal> | null>(null);
function showRelocateDialog() {
// This should work - does it work in proper Vue 3 or something?
// https://vuejs.org/guide/typescript/composition-api.html#typing-component-template-refs
// @ts-ignore
relocateModal.value?.showModal();
}
const store = useObjectStoreStore();
const { isLoaded, selectableObjectStores } = storeToRefs(store);
const currentObjectStore = computed<ConcreteObjectStoreModel | null>(() => {
const isLoadedVal = isLoaded.value;
const objectStores = selectableObjectStores.value;
const currentObjectStoreId = props.datasetStorageDetails.object_store_id;
if (!isLoadedVal) {
return null;
}
if (!objectStores) {
return null;
}
const filtered: ConcreteObjectStoreModel[] = objectStores.filter((objectStore) => objectStore.object_store_id == currentObjectStoreId);
return (filtered && filtered.length > 0) ? filtered[0] as ConcreteObjectStoreModel : null;
});
const validTargets = computed<ConcreteObjectStoreModel[]>(() => {
const isLoadedVal = isLoaded.value;
const objectStores = selectableObjectStores.value;
const currentObjectStoreId = props.datasetStorageDetails.object_store_id
if (!isLoadedVal) {
return [];
}
if (!objectStores) {
return [];
}
if (!currentObjectStore.value) {
return []
}
const currentDevice = currentObjectStore.value.device;
if (!currentDevice) {
return [];
}
const validTargets: ConcreteObjectStoreModel[] = objectStores.filter((objectStore) => objectStore.device == currentDevice && objectStore.object_store_id != currentObjectStoreId);
return validTargets as ConcreteObjectStoreModel[];
});
const relocatable = computed(() => { return validTargets.value.length > 0; });
</script>

<template>
<span class="storage-relocate-link">
<RelocateModal ref="relocateModal" v-if="currentObjectStore" :from-object-store="currentObjectStore" :target-object-stores="validTargets" />

Check failure on line 69 in client/src/components/Dataset/DatasetStorage/RelocateLink.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Attribute "v-if" should go before "ref"
<b-link v-if="relocatable" href="#" @click="showRelocateDialog">(relocate)</b-link>
</span>
</template>
42 changes: 42 additions & 0 deletions client/src/components/Dataset/DatasetStorage/RelocateModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import { ref } from "vue";

Check failure on line 2 in client/src/components/Dataset/DatasetStorage/RelocateModal.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!
import { ConcreteObjectStoreModel } from "@/api";
import RelocateDialog from "./RelocateDialog.vue";
interface RelocateModalProps {
fromObjectStore: ConcreteObjectStoreModel;
targetObjectStores: ConcreteObjectStoreModel[];
}
defineProps<RelocateModalProps>();
const show = ref(false);
function showModal() {
show.value = true;
}
const emit = defineEmits<{
(e: "relocate", value: string): void;
}>();
function relocate(objectStoreId: string) {
emit("relocate", objectStoreId);
}
const title = "Relocate Dataset Storage";
defineExpose({
showModal,
});
</script>

<template>
<b-modal v-model="show" hide-footer>
<template v-slot:modal-title>
<h2 class="mb-0">{{ title }}</h2>
<RelocateDialog :from-object-store="fromObjectStore" :target-object-stores="targetObjectStores" @relocate="relocate" />
</template>
</b-modal>
</template>
3 changes: 3 additions & 0 deletions lib/galaxy/webapps/galaxy/services/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class DatasetStorageDetails(Model):
badges: List[BadgeDict] = Field(
description="A list of badges describing object store properties for concrete object store dataset is stored in."
)
relocatable: bool = Field(description="Indicator of whether the objectstore for this dataset can be switched by this user.")


class DatasetInheritanceChainEntry(Model):
Expand Down Expand Up @@ -429,6 +430,7 @@ def show_storage(
source=quota_source.label,
enabled=quota_source.use,
)
relocatable = trans.app.security_agent.can_change_object_store_id(trans.user, dataset)
dataset_state = dataset.state
hashes = [h.to_dict() for h in dataset.hashes]
sources = [s.to_dict() for s in dataset.sources]
Expand All @@ -443,6 +445,7 @@ def show_storage(
sources=sources,
quota=quota,
badges=badges,
relocatable=relocatable,
)

def show_inheritance_chain(
Expand Down

0 comments on commit 3d33173

Please sign in to comment.