From 7c95ac20bd34ed1783ffb92c0f57d6256a006e94 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:56:57 +0200 Subject: [PATCH] Convert axios queries to fetcher + type fixes --- .../CurrentCollection/CollectionPanel.vue | 14 +- .../HistoryOperations/DefaultOperations.vue | 4 +- .../HistoryOperations/SelectionOperations.vue | 16 ++- .../src/components/History/model/queries.js | 125 ------------------ .../src/components/History/model/queries.ts | 102 ++++++++++++++ 5 files changed, 126 insertions(+), 135 deletions(-) delete mode 100644 client/src/components/History/model/queries.js create mode 100644 client/src/components/History/model/queries.ts diff --git a/client/src/components/History/CurrentCollection/CollectionPanel.vue b/client/src/components/History/CurrentCollection/CollectionPanel.vue index 825ba5a5baa5..7f6f211d6b20 100644 --- a/client/src/components/History/CurrentCollection/CollectionPanel.vue +++ b/client/src/components/History/CurrentCollection/CollectionPanel.vue @@ -68,12 +68,14 @@ const rootCollection = computed(() => { const isRoot = computed(() => dsc.value == rootCollection.value); const canEdit = computed(() => isRoot.value && canMutateHistory(props.history)); -function updateDsc(collection: any, fields: Object | undefined) { - updateContentFields(collection, fields).then((response) => { - Object.keys(response).forEach((key) => { - collection[key] = response[key]; - }); - }); +async function updateDsc(collection: CollectionEntry, fields: Object | undefined) { + if (!isHDCA(collection)) { + return; + } + const updatedCollection = await updateContentFields(collection, fields); + // Update only editable fields + collection.name = updatedCollection.name || collection.name; + collection.tags = updatedCollection.tags || collection.tags; } function getItemKey(item: DCESummary) { diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/DefaultOperations.vue b/client/src/components/History/CurrentHistory/HistoryOperations/DefaultOperations.vue index 07bb357e4c3a..c7c4350e617f 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/DefaultOperations.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/DefaultOperations.vue @@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { BDropdown, BDropdownItem, BDropdownText, BModal } from "bootstrap-vue"; import { toRef } from "vue"; -import type { HistorySummary, HistorySummaryExtended } from "@/api"; +import { type HistorySummaryExtended } from "@/api"; import { deleteAllHiddenContent, purgeAllDeletedContent, @@ -42,7 +42,7 @@ function purgeAllDeleted() { runOperation(() => purgeAllDeletedContent(props.history)); } -async function runOperation(operation: () => Promise) { +async function runOperation(operation: () => Promise) { emit("update:operation-running", props.history.update_time); await operation(); emit("update:operation-running", props.history.update_time); diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue index 59076e8063af..a7c5224de617 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue @@ -158,12 +158,14 @@ import { undeleteSelectedContent, unhideSelectedContent, } from "components/History/model/crud"; -import { createDatasetCollection, getHistoryContent } from "components/History/model/queries"; import { DatatypesProvider, DbKeyProvider } from "components/providers"; import SingleItemSelector from "components/SingleItemSelector"; import { StatelessTags } from "components/Tags"; +import { GalaxyApi } from "@/api"; +import { createDatasetCollection, filtersToQueryValues } from "@/components/History/model/queries"; import { useConfig } from "@/composables/config"; +import { rethrowSimple } from "@/utils/simple-error"; export default { components: { @@ -368,8 +370,18 @@ export default { async buildDatasetListAll() { let allContents = []; const filters = HistoryFilters.getQueryDict(this.filterText); + const filterQuery = filtersToQueryValues(filters); + const { data, error } = await GalaxyApi().GET("/api/histories/{history_id}/contents/{type}s", { + params: { + path: { history_id: this.history.id, type: "dataset" }, + query: { ...filterQuery, v: "dev" }, + }, + }); + if (error) { + rethrowSimple(error); + } - allContents = await getHistoryContent(this.history.id, filters, "dataset"); + allContents = data; this.buildNewCollection("list", allContents); }, diff --git a/client/src/components/History/model/queries.js b/client/src/components/History/model/queries.js deleted file mode 100644 index 305335cb533e..000000000000 --- a/client/src/components/History/model/queries.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Simple ajax queries that run against the api. - * - * A few history queries still hit routes that don't begin with /api. I have noted them - * in the comments. - */ - -import axios from "axios"; -import { prependPath } from "utils/redirect"; - -/** - * Generic json getter - * @param {*} response - */ - -const doResponse = (response) => { - if (response.status != 200) { - throw new Error(response); - } - return response.data; -}; - -/** - * Legacy query string rendering. (generates q/qv syntax queries). - * TODO: remove these converters when the api is modernized. - */ -function buildQueryStringFrom(filters) { - const queryString = Object.entries(filters) - .map(([f, v]) => `q=${f}&qv=${v}`) - .join("&"); - return queryString; -} - -// #endregion - -/** - * Content Queries - */ - -/** - * Deletes item from history - * - * @param {Object} content Content object - * @param {Boolean} purge Permanent delete - * @param {Boolean} recursive Scorch the earth? - */ -export async function deleteContent(content, deleteParams = {}) { - const defaults = { purge: false, recursive: false, stop_job: true }; - const params = Object.assign({}, defaults, deleteParams); - const { history_id, history_content_type, id } = content; - const url = `api/histories/${history_id}/contents/${history_content_type}s/${id}`; - const response = await axios.delete(prependPath(url), { params }); - return doResponse(response); -} - -/** - * Update specific fields on datasets or collections. - * @param {Object} content content object - * @param {Object} newFields key/value object of properties to update - */ -export async function updateContentFields(content, newFields = {}) { - const { history_id, id, history_content_type: type } = content; - const url = `api/histories/${history_id}/contents/${type}s/${id}`; - const response = await axios.put(prependPath(url), newFields); - return doResponse(response); -} - -/** Get all objects that match filters in a history - * - * @param {string} historyId - * @param {Object} filters - * @param {string} type - * - */ -export async function getHistoryContent(historyId, filters = {}, type = "dataset") { - const filterQuery = buildQueryStringFrom(filters); - const url = `api/histories/${historyId}/contents/${type}s?v=dev&${filterQuery}`; - const response = await axios.get(prependPath(url)); - return doResponse(response); -} - -/** - * Performs an operation on a specific set of items or all the items - * matching the filters. - * If a specific set of items is provided, the filters are ignored, otherwise - * the filters will determine which items are processed. - * - * @param {Object} history The history that contains the items - * @param {String} operation The operation to perform on all items - * @param {Object} filters The filter query parameters - * @param {Object[]} items The set of items to process as `{ id, history_content_type }` - * @param {Object} params Optional extra parameters passed to the operation - */ -export async function bulkUpdate(history, operation, filters, items = [], params = null) { - const { id } = history; - const filterQuery = buildQueryStringFrom(filters); - const url = `api/histories/${id}/contents/bulk?${filterQuery}`; - const payload = { - operation, - items, - params, - }; - const response = await axios.put(prependPath(url), payload); - return doResponse(response); -} - -/** - * Collections - */ - -export async function createDatasetCollection(history, inputs = {}) { - const defaults = { - collection_type: "list", - copy_elements: true, - name: "list", - element_identifiers: [], - hide_source_items: true, - type: "dataset_collection", - }; - - const payload = Object.assign({}, defaults, inputs); - const url = `api/histories/${history.id}/contents`; - const response = await axios.post(prependPath(url), payload); - return doResponse(response); -} diff --git a/client/src/components/History/model/queries.ts b/client/src/components/History/model/queries.ts new file mode 100644 index 000000000000..5cad0faee73e --- /dev/null +++ b/client/src/components/History/model/queries.ts @@ -0,0 +1,102 @@ +import { type components, GalaxyApi, type HistoryItemSummary, type HistorySummary } from "@/api"; +import { rethrowSimple } from "@/utils/simple-error"; + +type BulkOperation = components["schemas"]["HistoryContentItemOperation"]; +type QueryFilters = Record; + +export function filtersToQueryValues(filters: QueryFilters) { + const filterKeys = Object.keys(filters); + const filterValues = filterKeys.map((key) => `${filters[key]}`); + return { q: filterKeys, qv: filterValues }; +} + +/** + * Deletes item from history + */ +export async function deleteContent( + content: HistoryItemSummary, + deleteParams: Partial<{ purge: boolean; recursive: boolean }> = {} +) { + const defaults = { purge: false, recursive: false, stop_job: true }; + const params = Object.assign({}, defaults, deleteParams); + const { data, error } = await GalaxyApi().DELETE("/api/histories/{history_id}/contents/{type}s/{id}", { + params: { + path: { history_id: content.history_id, type: content.history_content_type, id: content.id }, + query: params, + }, + }); + + if (error) { + rethrowSimple(error); + } + return data; +} + +/** + * Update specific fields on datasets or collections. + */ +export async function updateContentFields(content: HistoryItemSummary, newFields = {}) { + const { data, error } = await GalaxyApi().PUT("/api/histories/{history_id}/contents/{type}s/{id}", { + params: { + path: { history_id: content.history_id, type: content.history_content_type, id: content.id }, + }, + body: newFields, + }); + + if (error) { + rethrowSimple(error); + } + return data; +} + +/** + * Performs an operation on a specific set of items or all the items + * matching the filters. + * If a specific set of items is provided, the filters are ignored, otherwise + * the filters will determine which items are processed. + */ +export async function bulkUpdate( + history: HistorySummary, + operation: BulkOperation, + filters: QueryFilters, + items = [], + params = null +) { + const { data, error } = await GalaxyApi().PUT("/api/histories/{history_id}/contents/bulk", { + params: { + path: { history_id: history.id }, + query: filtersToQueryValues(filters), + }, + body: { + operation, + items, + params, + }, + }); + + if (error) { + rethrowSimple(error); + } + return data; +} + +export async function createDatasetCollection(history: HistorySummary, inputs = {}) { + const defaults = { + collection_type: "list", + copy_elements: true, + name: "list", + element_identifiers: [], + hide_source_items: true, + }; + const payload = Object.assign({}, defaults, inputs); + + const { data, error } = await GalaxyApi().POST("/api/histories/{history_id}/contents", { + params: { path: { history_id: history.id } }, + body: { ...payload, instance_type: "history", type: "dataset_collection" }, + }); + + if (error) { + rethrowSimple(error); + } + return data; +}