diff --git a/client/src/api/datasets.ts b/client/src/api/datasets.ts index 642aa0c03a22..63b8b5714d9c 100644 --- a/client/src/api/datasets.ts +++ b/client/src/api/datasets.ts @@ -38,6 +38,8 @@ export async function getDatasets(options: GetDatasetsOptions = {}) { export const fetchDataset = fetcher.path("/api/datasets/{dataset_id}").method("get").create(); +export const fetchDatasetStorage = fetcher.path("/api/datasets/{dataset_id}/storage").method("get").create(); + export async function fetchDatasetDetails(params: { id: string }): Promise { const { data } = await fetchDataset({ dataset_id: params.id, view: "detailed" }); // We know that the server will return a DatasetDetails object because of the view parameter diff --git a/client/src/api/index.ts b/client/src/api/index.ts index 0003833819fa..83da2479adbc 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -27,6 +27,11 @@ export type DatasetSummary = components["schemas"]["HDASummary"]; */ export type DatasetDetails = components["schemas"]["HDADetailed"]; +/** + * Contains storage (object store, quota, etc..) details for a dataset. + */ +export type DatasetStorageDetails = components["schemas"]["DatasetStorageDetails"]; + /** * Represents a HistoryDatasetAssociation with either summary or detailed information. */ diff --git a/client/src/api/schema/__mocks__/fetcher.ts b/client/src/api/schema/__mocks__/fetcher.ts index 5af41cc7e146..56c8a56ed3ef 100644 --- a/client/src/api/schema/__mocks__/fetcher.ts +++ b/client/src/api/schema/__mocks__/fetcher.ts @@ -48,7 +48,8 @@ function getMockReturn(path: Path, method: Method, args: any[]) { } } - return null; + // if no mock has been setup, never resolve API request + return new Promise(() => {}); } function setMockReturn(path: Path | RegExp, method: Method, value: any) { diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 3a2bbc468ebb..3207d1fb351c 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -2925,6 +2925,19 @@ export interface components { private: boolean; quota: components["schemas"]["QuotaModel"]; }; + /** ConcreteObjectStoreQuotaSourceDetails */ + ConcreteObjectStoreQuotaSourceDetails: { + /** + * Enabled + * @description Whether the object store tracks quota on the data (independent of Galaxy's configuration) + */ + enabled: boolean; + /** + * Source + * @description The quota source label corresponding to the object store the dataset is stored in (or would be stored in) + */ + source: string | null; + }; /** ContentsObject */ ContentsObject: { /** @@ -3775,9 +3788,9 @@ export interface components { DatasetStorageDetails: { /** * Badges - * @description A mapping of object store labels to badges describing object store properties. + * @description A list of badges describing object store properties for concrete object store dataset is stored in. */ - badges: Record[]; + badges: components["schemas"]["BadgeDict"][]; /** * Dataset State * @description The model state of the supplied dataset instance. @@ -3808,11 +3821,8 @@ export interface components { * @description The percentage indicating how full the store is. */ percent_used: number | null; - /** - * Quota - * @description Information about quota sources around dataset storage. - */ - quota: Record; + /** @description Information about quota sources around dataset storage. */ + quota: components["schemas"]["ConcreteObjectStoreQuotaSourceDetails"]; /** * Shareable * @description Is this dataset shareable. diff --git a/client/src/components/Dataset/DatasetStorage/DatasetStorage.test.js b/client/src/components/Dataset/DatasetStorage/DatasetStorage.test.js index f8218abbe934..1a8a5a7cf7f1 100644 --- a/client/src/components/Dataset/DatasetStorage/DatasetStorage.test.js +++ b/client/src/components/Dataset/DatasetStorage/DatasetStorage.test.js @@ -1,11 +1,13 @@ import { shallowMount } from "@vue/test-utils"; -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; import flushPromises from "flush-promises"; import { getLocalVue } from "tests/jest/helpers"; +import { mockFetcher } from "@/api/schema/__mocks__"; + import DatasetStorage from "./DatasetStorage"; +jest.mock("@/api/schema"); + const localVue = getLocalVue(); const TEST_STORAGE_API_RESPONSE_WITHOUT_ID = { @@ -13,17 +15,12 @@ const TEST_STORAGE_API_RESPONSE_WITHOUT_ID = { private: false, }; const TEST_DATASET_ID = "1"; -const TEST_STORAGE_URL = `/api/datasets/${TEST_DATASET_ID}/storage`; +const STORAGE_FETCH_URL = "/api/datasets/{dataset_id}/storage"; const TEST_ERROR_MESSAGE = "Opps all errors."; describe("DatasetStorage.vue", () => { - let axiosMock; let wrapper; - beforeEach(async () => { - axiosMock = new MockAdapter(axios); - }); - function mount() { wrapper = shallowMount(DatasetStorage, { propsData: { datasetId: TEST_DATASET_ID }, @@ -32,7 +29,7 @@ describe("DatasetStorage.vue", () => { } async function mountWithResponse(response) { - axiosMock.onGet(TEST_STORAGE_URL).reply(200, response); + mockFetcher.path(STORAGE_FETCH_URL).method("get").mock({ data: response }); mount(); await flushPromises(); } @@ -45,9 +42,12 @@ describe("DatasetStorage.vue", () => { }); it("test error rendering...", async () => { - axiosMock.onGet(TEST_STORAGE_URL).reply(400, { - err_msg: TEST_ERROR_MESSAGE, - }); + mockFetcher + .path(STORAGE_FETCH_URL) + .method("get") + .mock(() => { + throw Error(TEST_ERROR_MESSAGE); + }); mount(); await flushPromises(); expect(wrapper.findAll(".error").length).toBe(1); @@ -59,10 +59,5 @@ describe("DatasetStorage.vue", () => { await mountWithResponse(TEST_STORAGE_API_RESPONSE_WITHOUT_ID); expect(wrapper.findAll("loadingspan-stub").length).toBe(0); expect(wrapper.findAll("describeobjectstore-stub").length).toBe(1); - expect(wrapper.vm.storageInfo.private).toEqual(false); - }); - - afterEach(() => { - axiosMock.restore(); }); }); diff --git a/client/src/components/Dataset/DatasetStorage/DatasetStorage.vue b/client/src/components/Dataset/DatasetStorage/DatasetStorage.vue index 6e8dd47508e7..90706afe28ea 100644 --- a/client/src/components/Dataset/DatasetStorage/DatasetStorage.vue +++ b/client/src/components/Dataset/DatasetStorage/DatasetStorage.vue @@ -1,3 +1,63 @@ + + - - diff --git a/client/src/components/HistoryExport/Index.vue b/client/src/components/HistoryExport/Index.vue index dfbe79cf5dc9..2b1552006b54 100644 --- a/client/src/components/HistoryExport/Index.vue +++ b/client/src/components/HistoryExport/Index.vue @@ -5,6 +5,7 @@ import { useFileSources } from "@/composables/fileSources"; import ToLink from "./ToLink.vue"; import ToRemoteFile from "./ToRemoteFile.vue"; +import LoadingSpan from "@/components/LoadingSpan.vue"; const { isLoading: initializingFileSources, hasWritable: hasWritableFileSources } = useFileSources(); @@ -18,7 +19,7 @@ const props = defineProps();

Export history archive

- + diff --git a/client/src/components/LoadingSpan.vue b/client/src/components/LoadingSpan.vue index c4c3b9d26b5e..3babd0a618d7 100644 --- a/client/src/components/LoadingSpan.vue +++ b/client/src/components/LoadingSpan.vue @@ -6,6 +6,7 @@ + +