From cb67b8e7faacdecc6b14fbefc903847ab2762f9c Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:52:19 +0200 Subject: [PATCH] Replace fetcher in PageEditor/ObjectPermissions.vue --- client/src/api/histories.ts | 2 - client/src/api/index.ts | 5 + client/src/api/invocations.ts | 5 - client/src/api/jobs.ts | 3 - client/src/api/workflows.ts | 3 - .../PageEditor/ObjectPermissions.vue | 164 +++++++++++------- client/src/stores/invocationStore.ts | 15 +- 7 files changed, 116 insertions(+), 81 deletions(-) diff --git a/client/src/api/histories.ts b/client/src/api/histories.ts index 5eafe24bd965..28e0879e7273 100644 --- a/client/src/api/histories.ts +++ b/client/src/api/histories.ts @@ -11,5 +11,3 @@ export const updateHistoryItemsInBulk = fetcher .path("/api/histories/{history_id}/contents/bulk") .method("put") .create(); -export const sharing = fetcher.path("/api/histories/{history_id}/sharing").method("get").create(); -export const enableLink = fetcher.path("/api/histories/{history_id}/enable_link_access").method("put").create(); diff --git a/client/src/api/index.ts b/client/src/api/index.ts index 087aec8e3b12..f8d26ffcd2b3 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -276,3 +276,8 @@ export type DatasetTransform = { action: "to_posix_lines" | "spaces_to_tabs" | "datatype_groom"; datatype_ext: "bam" | "qname_sorted.bam" | "qname_input_sorted.bam" | "isa-tab" | "isa-json"; }; + +/** + * Base type for all exceptions returned by the API. + */ +export type MessageException = components["schemas"]["MessageExceptionModel"]; diff --git a/client/src/api/invocations.ts b/client/src/api/invocations.ts index ce9023d0c4be..4d3d9cfcc426 100644 --- a/client/src/api/invocations.ts +++ b/client/src/api/invocations.ts @@ -39,11 +39,6 @@ export async function invocationForJob(params: { jobId: string }): Promise { - const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}`); - return data as WorkflowInvocation; -} - export async function fetchInvocationJobsSummary(params: { id: string }): Promise { const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}/jobs_summary`); return data as WorkflowInvocationJobsSummary; diff --git a/client/src/api/jobs.ts b/client/src/api/jobs.ts index 2595814714d6..1aa210cfe6d0 100644 --- a/client/src/api/jobs.ts +++ b/client/src/api/jobs.ts @@ -1,9 +1,6 @@ import { components, fetcher } from "@/api/schema"; export type JobDestinationParams = components["schemas"]["JobDestinationParams"]; - -export const getJobDetails = fetcher.path("/api/jobs/{job_id}").method("get").create(); - export type ShowFullJobResponse = components["schemas"]["ShowFullJobResponse"]; export type JobDetails = components["schemas"]["ShowFullJobResponse"] | components["schemas"]["EncodedJobDetails"]; export const fetchJobDetails = fetcher.path("/api/jobs/{job_id}").method("get").create(); diff --git a/client/src/api/workflows.ts b/client/src/api/workflows.ts index f9e1642ab581..bef10b23d386 100644 --- a/client/src/api/workflows.ts +++ b/client/src/api/workflows.ts @@ -5,6 +5,3 @@ export type StoredWorkflowDetailed = components["schemas"]["StoredWorkflowDetail export const workflowFetcher = fetcher.path("/api/workflows/{workflow_id}").method("get").create(); export const invocationCountsFetcher = fetcher.path("/api/workflows/{workflow_id}/counts").method("get").create(); - -export const sharing = fetcher.path("/api/workflows/{workflow_id}/sharing").method("get").create(); -export const enableLink = fetcher.path("/api/workflows/{workflow_id}/enable_link_access").method("put").create(); diff --git a/client/src/components/PageEditor/ObjectPermissions.vue b/client/src/components/PageEditor/ObjectPermissions.vue index d9d548755337..a71e81b5a7a5 100644 --- a/client/src/components/PageEditor/ObjectPermissions.vue +++ b/client/src/components/PageEditor/ObjectPermissions.vue @@ -2,11 +2,8 @@ import axios from "axios"; import Vue, { computed, Ref, ref, watch } from "vue"; +import { client, type MessageException } from "@/api"; import { fetchCollectionSummary } from "@/api/datasetCollections"; -import { enableLink, sharing } from "@/api/histories"; -import { fetchInvocationDetails } from "@/api/invocations"; -import { getJobDetails } from "@/api/jobs"; -import { enableLink as enableLinkWorkflow, sharing as sharingWorkflow } from "@/api/workflows"; import { useToast } from "@/composables/toast"; import { useDatasetStore } from "@/stores/datasetStore"; import { useHistoryStore } from "@/stores/historyStore"; @@ -59,49 +56,62 @@ const workflowAccessible: AccessibleMapRef = ref({}); const historyDatasetAccessible: AccessibleMapRef = ref({}); function catchErrorToToast(title: string, prolog: string) { - function handleError(e: Error) { + function handleError(e: Error | MessageException) { toast.error(`${prolog} Reason: ${errorMessageAsString(e)}.`, title); } return handleError; } watch(referencedJobIds, async () => { - referencedJobIds.value.forEach((jobId) => { + referencedJobIds.value.forEach(async (jobId) => { if (jobId in jobsToHistories.value) { return; } + const handleError = catchErrorToToast( "Failed to job information", "Some referenced objects may not be listed." ); - getJobDetails({ job_id: jobId }) - .then(({ data }) => { - if ("history_id" in data) { - const historyId = data.history_id; - Vue.set(jobsToHistories.value, jobId, historyId); - } - }) - .catch(handleError); + const { data, error } = await client.GET("/api/jobs/{job_id}", { + params: { path: { job_id: jobId } }, + }); + + if (error) { + handleError(error); + return; + } + + if ("history_id" in data) { + const historyId = data.history_id; + Vue.set(jobsToHistories.value, jobId, historyId); + } }); }); watch(referencedInvocationIds, async () => { - referencedInvocationIds.value.forEach((invocationId) => { + referencedInvocationIds.value.forEach(async (invocationId) => { if (invocationId in invocationsToHistories.value) { return; } + const handleError = catchErrorToToast( "Failed to fetch workflow information", "Some referenced objects may not be listed." ); - fetchInvocationDetails({ id: invocationId }) - .then(({ data }) => { - if ("history_id" in data) { - const historyId = data.history_id; - Vue.set(invocationsToHistories.value, invocationId, historyId); - } - }) - .catch(handleError); + + const { data, error } = await client.GET("/api/invocations/{invocation_id}", { + params: { path: { invocation_id: invocationId } }, + }); + + if (error) { + handleError(error); + return; + } + + if ("history_id" in data) { + const historyId = data.history_id; + Vue.set(invocationsToHistories.value, invocationId, historyId); + } }); }); @@ -181,42 +191,50 @@ watch( { immediate: true } ); -watch(historyIds, () => { +watch(historyIds, async () => { for (const historyId of historyIds.value) { loadHistoryById(historyId); if (historyId && !(historyId in historyAccessible.value)) { Vue.set(historyAccessible.value, historyId, null); - sharing({ history_id: historyId }) - .then((response) => { - const accessible = response.data.importable; - Vue.set(historyAccessible.value, historyId, accessible); - }) - .catch((e) => { - const errorMessage = errorMessageAsString(e); - const title = "Failed to fetch history metadata."; - toast.error(errorMessage, title); - Vue.set(historyAccessible.value, historyId, `${title} Reason: ${errorMessage}.`); - }); + + const { data, error } = await client.GET("/api/histories/{history_id}/sharing", { + params: { path: { history_id: historyId } }, + }); + + if (error) { + const errorMessage = errorMessageAsString(error); + const title = "Failed to fetch history metadata."; + toast.error(errorMessage, title); + Vue.set(historyAccessible.value, historyId, `${title} Reason: ${errorMessage}.`); + return; + } + + const accessible = data.importable; + Vue.set(historyAccessible.value, historyId, accessible); } } }); -function initWorkflowData() { +async function initWorkflowData() { for (const workflowId of referencedWorkflowIds.value) { fetchWorkflowForInstanceId(workflowId); if (workflowId && !(workflowId in workflowAccessible.value)) { Vue.set(workflowAccessible.value, workflowId, null); - sharingWorkflow({ workflow_id: workflowId }) - .then((response) => { - const accessible = response.data.importable; - Vue.set(workflowAccessible.value, workflowId, accessible); - }) - .catch((e) => { - const errorMessage = errorMessageAsString(e); - const title = "Failed to fetch workflow metadata."; - toast.error(errorMessage, title); - Vue.set(workflowAccessible.value, workflowId, `${title} Reason: ${errorMessage}.`); - }); + + const { data, error } = await client.GET("/api/workflows/{workflow_id}/sharing", { + params: { path: { workflow_id: workflowId } }, + }); + + if (error) { + const errorMessage = errorMessageAsString(error); + const title = "Failed to fetch workflow metadata."; + toast.error(errorMessage, title); + Vue.set(workflowAccessible.value, workflowId, `${title} Reason: ${errorMessage}.`); + return; + } + + const accessible = data.importable; + Vue.set(workflowAccessible.value, workflowId, accessible); } } } @@ -256,35 +274,49 @@ const tableItems = computed(() => { return [...histories.value, ...workflows.value, ...datasets.value]; }); -function makeAccessible(item: ItemInterface) { - let promise; +async function makeAccessible(item: ItemInterface) { + let accessibleResult: Boolean | undefined = undefined; + let errorResult: MessageException | undefined = undefined; let accessibleMap: AccessibleMapRef; if (item.type == "history") { - promise = enableLink({ history_id: item.id }); + const { data, error } = await client.PUT("/api/histories/{history_id}/enable_link_access", { + params: { path: { history_id: item.id } }, + }); + + errorResult = error; + accessibleResult = data?.importable; accessibleMap = historyAccessible; } else if (item.type == "workflow") { - promise = enableLinkWorkflow({ workflow_id: item.id }); + const { data, error } = await client.PUT("/api/workflows/{workflow_id}/enable_link_access", { + params: { path: { workflow_id: item.id } }, + }); + + errorResult = error; + accessibleResult = data?.importable; accessibleMap = workflowAccessible; } else if (item.type == "historyDataset") { - const data = { - dataset_id: item.id, - action: "remove_restrictions", - }; - promise = axios.put(withPrefix(`/api/datasets/${item.id}/permissions`), data); + const { data, error } = await client.PUT("/api/datasets/{dataset_id}/permissions", { + params: { path: { dataset_id: item.id } }, + body: { + action: "remove_restrictions", + }, + }); + + errorResult = error; + accessibleResult = data !== undefined; accessibleMap = historyDatasetAccessible; - } - if (!promise) { + } else { console.log("Serious client programming error - unknown object type encountered."); return; } - promise - .then(() => Vue.set(accessibleMap.value, item.id, true)) - .catch((e) => { - const errorMessage = errorMessageAsString(e); - const title = "Failed update object accessibility."; - toast.error(errorMessage, title); - Vue.set(accessibleMap.value, item.id, `${title} Reason: ${errorMessage}.`); - }); + if (errorResult) { + const errorMessage = errorMessageAsString(errorResult); + const title = "Failed update object accessibility."; + toast.error(errorMessage, title); + Vue.set(accessibleMap.value, item.id, `${title} Reason: ${errorMessage}.`); + return; + } + Vue.set(accessibleMap.value, item.id, accessibleResult); } diff --git a/client/src/stores/invocationStore.ts b/client/src/stores/invocationStore.ts index ceac2109f459..5174fecc9998 100644 --- a/client/src/stores/invocationStore.ts +++ b/client/src/stores/invocationStore.ts @@ -1,16 +1,27 @@ import { defineStore } from "pinia"; +import { client } from "@/api"; import { - fetchInvocationDetails, fetchInvocationJobsSummary, fetchInvocationStep, type WorkflowInvocation, type WorkflowInvocationJobsSummary, type WorkflowInvocationStep, } from "@/api/invocations"; -import { useKeyedCache } from "@/composables/keyedCache"; +import { type FetchParams, useKeyedCache } from "@/composables/keyedCache"; +import { rethrowSimple } from "@/utils/simple-error"; export const useInvocationStore = defineStore("invocationStore", () => { + async function fetchInvocationDetails(params: FetchParams): Promise { + const { data, error } = await client.GET("/api/invocations/{invocation_id}", { + params: { path: { invocation_id: params.id } }, + }); + if (error) { + rethrowSimple(error); + } + return data; + } + const { getItemById: getInvocationById, fetchItemById: fetchInvocationForId } = useKeyedCache(fetchInvocationDetails);