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

Modernize bits and pieces of storage display #17436

Merged
merged 5 commits into from
Feb 14, 2024
Merged
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
2 changes: 2 additions & 0 deletions client/src/api/datasets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DatasetDetails> {
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
Expand Down
5 changes: 5 additions & 0 deletions client/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
3 changes: 2 additions & 1 deletion client/src/api/schema/__mocks__/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
interface MockValue {
path: Path | RegExp;
method: Method;
value: any;

Check warning on line 17 in client/src/api/schema/__mocks__/fetcher.ts

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Unexpected any. Specify a different type
}

const mockValues: MockValue[] = [];

function getMockReturn(path: Path, method: Method, args: any[]) {

Check warning on line 22 in client/src/api/schema/__mocks__/fetcher.ts

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Unexpected any. Specify a different type
for (let i = mockValues.length - 1; i >= 0; i--) {
const matchPath = mockValues[i]!.path;
const matchMethod = mockValues[i]!.method;
Expand Down Expand Up @@ -48,10 +48,11 @@
}
}

return null;
// if no mock has been setup, never resolve API request
return new Promise(() => {});
}

function setMockReturn(path: Path | RegExp, method: Method, value: any) {

Check warning on line 55 in client/src/api/schema/__mocks__/fetcher.ts

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Unexpected any. Specify a different type
mockValues.push({
path,
method,
Expand Down Expand Up @@ -82,8 +83,8 @@
path: (path: Path | RegExp) => ({
method: (method: Method) => ({
// prettier-ignore
create: () => async (...args: any[]) => getMockReturn(path as Path, method, args),

Check warning on line 86 in client/src/api/schema/__mocks__/fetcher.ts

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Unexpected any. Specify a different type
mock: (mockReturn: any) => setMockReturn(path, method, mockReturn),

Check warning on line 87 in client/src/api/schema/__mocks__/fetcher.ts

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Unexpected any. Specify a different type
}),
}),
clearMocks: () => {
Expand Down
24 changes: 17 additions & 7 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2918,6 +2918,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: {
/**
Expand Down Expand Up @@ -3768,9 +3781,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<string, never>[];
badges: components["schemas"]["BadgeDict"][];
/**
* Dataset State
* @description The model state of the supplied dataset instance.
Expand Down Expand Up @@ -3801,11 +3814,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<string, never>;
/** @description Information about quota sources around dataset storage. */
quota: components["schemas"]["ConcreteObjectStoreQuotaSourceDetails"];
/**
* Shareable
* @description Is this dataset shareable.
Expand Down
29 changes: 12 additions & 17 deletions client/src/components/Dataset/DatasetStorage/DatasetStorage.test.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
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 = {
object_store_id: null,
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 },
Expand All @@ -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();
}
Expand All @@ -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);
Expand All @@ -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();
});
});
129 changes: 60 additions & 69 deletions client/src/components/Dataset/DatasetStorage/DatasetStorage.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";

import { DatasetStorageDetails } from "@/api";
import { fetchDatasetStorage } from "@/api/datasets";
import { errorMessageAsString } from "@/utils/simple-error";

import LoadingSpan from "@/components/LoadingSpan.vue";
import DescribeObjectStore from "@/components/ObjectStore/DescribeObjectStore.vue";

interface DatasetStorageProps {
datasetId: string;
datasetType?: "hda" | "ldda";
includeTitle?: boolean;
}

const props = withDefaults(defineProps<DatasetStorageProps>(), {
datasetType: "hda",
includeTitle: true,
});

const storageInfo = ref<DatasetStorageDetails | null>(null);
const errorMessage = ref<string | null>(null);

const discarded = computed(() => {
return storageInfo.value?.dataset_state == "discarded";
});

const deferred = computed(() => {
return storageInfo.value?.dataset_state == "deferred";
});

const sourceUri = computed(() => {
const sources = storageInfo.value?.sources;
if (!sources) {
return null;
}
const rootSources = sources.filter((source) => !source.extra_files_path);
if (rootSources.length == 0) {
return null;
}
return rootSources[0]?.source_uri;
});

watch(
props,
async () => {
const datasetId = props.datasetId;
const datasetType = props.datasetType;
try {
const response = await fetchDatasetStorage({ dataset_id: datasetId, hda_ldda: datasetType });
storageInfo.value = response.data;
} catch (error) {
errorMessage.value = errorMessageAsString(error);
}
},
{ immediate: true }
);
</script>

<template>
<div>
<h2 v-if="includeTitle" class="h-md">Dataset Storage</h2>
Expand All @@ -20,72 +80,3 @@
</div>
</div>
</template>

<script>
import axios from "axios";
import LoadingSpan from "components/LoadingSpan";
import DescribeObjectStore from "components/ObjectStore/DescribeObjectStore";
import { getAppRoot } from "onload/loadConfig";
import { errorMessageAsString } from "utils/simple-error";

export default {
components: {
DescribeObjectStore,
LoadingSpan,
},
props: {
datasetId: {
type: String,
},
datasetType: {
type: String,
default: "hda",
},
includeTitle: {
type: Boolean,
default: true,
},
},
data() {
return {
storageInfo: null,
errorMessage: null,
};
},
computed: {
discarded() {
return this.storageInfo.dataset_state == "discarded";
},
deferred() {
return this.storageInfo.dataset_state == "deferred";
},
sourceUri() {
const sources = this.storageInfo.sources;
if (!sources) {
return null;
}
const rootSources = sources.filter((source) => !source.extra_files_path);
if (rootSources.length == 0) {
return null;
}
return rootSources[0].source_uri;
},
},
created() {
const datasetId = this.datasetId;
const datasetType = this.datasetType;
axios
.get(`${getAppRoot()}api/datasets/${datasetId}/storage`, { hda_ldda: datasetType })
.then(this.handleResponse)
.catch((errorMessage) => {
this.errorMessage = errorMessageAsString(errorMessage);
});
},
methods: {
handleResponse(response) {
const storageInfo = response.data;
this.storageInfo = storageInfo;
},
},
};
</script>
3 changes: 2 additions & 1 deletion client/src/components/HistoryExport/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -18,7 +19,7 @@ const props = defineProps<ExportHistoryProps>();
<span class="history-export-component">
<h1 class="h-lg">Export history archive</h1>
<span v-if="initializingFileSources">
<loading-span message="Loading file sources configuration from Galaxy server." />
<LoadingSpan message="Loading file sources configuration from Galaxy server." />
</span>
<span v-else-if="hasWritableFileSources">
<BCard no-body>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/LoadingSpan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</template>
<script>
export default {
name: "LoadingSpan",
props: {
classes: {
type: String,
Expand Down
6 changes: 6 additions & 0 deletions client/src/components/ObjectStore/DescribeObjectStore.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ defineExpose({
});
</script>

<script lang="ts">
export default {
name: "DescribeObjectStore",
};
</script>

<template>
<div>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ describe("ShowSelectedObjectStore", () => {
wrapper = mount(ShowSelectedObjectStore, {
propsData: { preferredObjectStoreId: TEST_OBJECT_ID, forWhat: "Data goes into..." },
localVue,
stubs: {
LoadingSpan: true,
DescribeObjectStore: true,
},
});
let loadingEl = wrapper.findComponent(LoadingSpan);
expect(loadingEl.exists()).toBeTruthy();
Expand Down
14 changes: 1 addition & 13 deletions client/src/components/User/DiskUsage/DiskUsageSummary.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { mount } from "@vue/test-utils";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import flushPromises from "flush-promises";
import { createPinia } from "pinia";
import { getLocalVue } from "tests/jest/helpers";
Expand Down Expand Up @@ -69,16 +67,6 @@ async function mountDiskUsageSummaryWrapper(enableQuotas: boolean) {
}

describe("DiskUsageSummary.vue", () => {
let axiosMock: MockAdapter;

beforeEach(async () => {
axiosMock = new MockAdapter(axios);
});

afterEach(async () => {
axiosMock.reset();
});

it("should display basic disk usage summary if quotas are NOT enabled", async () => {
const enableQuotasInConfig = false;
const wrapper = await mountDiskUsageSummaryWrapper(enableQuotasInConfig);
Expand Down Expand Up @@ -115,7 +103,7 @@ describe("DiskUsageSummary.vue", () => {
},
];
mockFetcher.path("/api/users/{user_id}/usage").method("get").mock({ data: updatedFakeQuotaUsages });
axiosMock.onGet(`/api/tasks/${FAKE_TASK_ID}/state`).reply(200, "SUCCESS");
mockFetcher.path("/api/tasks/{task_id}/state").method("get").mock({ data: "SUCCESS" });
const refreshButton = wrapper.find("#refresh-disk-usage");
await refreshButton.trigger("click");
const refreshingAlert = wrapper.find(".refreshing-alert");
Expand Down
Loading
Loading