Skip to content

Commit

Permalink
Replace fetcher in PageEditor/ObjectPermissions.vue
Browse files Browse the repository at this point in the history
  • Loading branch information
davelopez committed Jul 18, 2024
1 parent f1f8574 commit cb67b8e
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 81 deletions.
2 changes: 0 additions & 2 deletions client/src/api/histories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
5 changes: 5 additions & 0 deletions client/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
5 changes: 0 additions & 5 deletions client/src/api/invocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ export async function invocationForJob(params: { jobId: string }): Promise<Workf
}

// TODO: Replace these provisional functions with fetchers after https://github.com/galaxyproject/galaxy/pull/16707 is merged
export async function fetchInvocationDetails(params: { id: string }): Promise<WorkflowInvocation> {
const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}`);
return data as WorkflowInvocation;
}

export async function fetchInvocationJobsSummary(params: { id: string }): Promise<WorkflowInvocationJobsSummary> {
const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}/jobs_summary`);
return data as WorkflowInvocationJobsSummary;
Expand Down
3 changes: 0 additions & 3 deletions client/src/api/jobs.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
3 changes: 0 additions & 3 deletions client/src/api/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
164 changes: 98 additions & 66 deletions client/src/components/PageEditor/ObjectPermissions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
}
});
});
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -256,35 +274,49 @@ const tableItems = computed<ItemInterface[]>(() => {
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);
}
</script>

Expand Down
15 changes: 13 additions & 2 deletions client/src/stores/invocationStore.ts
Original file line number Diff line number Diff line change
@@ -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<WorkflowInvocation> {
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<WorkflowInvocation>(fetchInvocationDetails);

Expand Down

0 comments on commit cb67b8e

Please sign in to comment.