From 9a77f3630cbf658e5aea430327ace47141f76663 Mon Sep 17 00:00:00 2001 From: guerler Date: Sat, 2 Dec 2023 19:51:45 +0300 Subject: [PATCH 1/8] Switch groups grid provider to grid data only provider --- lib/galaxy/web/framework/helpers/grids.py | 6 -- .../webapps/galaxy/controllers/admin.py | 89 ++++++++----------- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/lib/galaxy/web/framework/helpers/grids.py b/lib/galaxy/web/framework/helpers/grids.py index d34aac73829f..1306b7306f6f 100644 --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -1114,9 +1114,3 @@ def get_current_item(self, trans, **kwargs): def build_initial_query(self, trans, **kwargs): return trans.sa_session.query(self.model_class) - - def apply_query_filter(self, trans, query, **kwargs): - # Applies a database filter that holds for all items in the grid. - # (gvk) Is this method necessary? Why not simply build the entire query, - # including applying filters in the build_initial_query() method? - return query diff --git a/lib/galaxy/webapps/galaxy/controllers/admin.py b/lib/galaxy/webapps/galaxy/controllers/admin.py index f6068d87df13..a6239cf46c03 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin.py @@ -224,17 +224,7 @@ def apply_query_filter(self, query, **kwargs): return query -class GroupListGrid(grids.Grid): - class NameColumn(grids.TextColumn): - def get_value(self, trans, grid, group): - return escape(group.name) - - class StatusColumn(grids.GridColumn): - def get_value(self, trans, grid, group): - if group.deleted: - return "deleted" - return "" - +class GroupListGrid(grids.GridData): class RolesColumn(grids.GridColumn): def get_value(self, trans, grid, group): if group.roles: @@ -253,51 +243,42 @@ def get_value(self, trans, grid, group): model_class = model.Group default_sort_key = "name" columns = [ - NameColumn( - "Name", - key="name", - link=(lambda item: dict(action="form/manage_users_and_roles_for_group", id=item.id, webapp="galaxy")), - model_class=model.Group, - attach_popup=True, - filterable="advanced", - ), - UsersColumn("Users", attach_popup=False), - RolesColumn("Roles", attach_popup=False), - StatusColumn("Status", attach_popup=False), - # Columns that are valid for filtering but are not visible. - grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced"), + grids.GridColumn("Name", key="name"), + UsersColumn("Users", key="users"), + RolesColumn("Roles", key="roles"), + grids.DeletedColumn("Deleted", key="deleted", escape=False), grids.GridColumn("Last Updated", key="update_time", format=pretty_print_time_interval), ] - columns.append( - grids.MulticolFilterColumn( - "Search", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard" - ) - ) - global_actions = [grids.GridAction("Add new group", url_args=dict(action="form/create_group"))] - operations = [ - grids.GridOperation( - "Edit Name", - condition=(lambda item: not item.deleted), - allow_multiple=False, - url_args=dict(action="form/rename_group"), - ), - grids.GridOperation( - "Edit Permissions", - condition=(lambda item: not item.deleted), - allow_multiple=False, - url_args=dict(action="form/manage_users_and_roles_for_group", webapp="galaxy"), - ), - grids.GridOperation("Delete", condition=(lambda item: not item.deleted), allow_multiple=True), - grids.GridOperation("Undelete", condition=(lambda item: item.deleted), allow_multiple=True), - grids.GridOperation("Purge", condition=(lambda item: item.deleted), allow_multiple=True), - ] - standard_filters = [ - grids.GridColumnFilter("Active", args=dict(deleted=False)), - grids.GridColumnFilter("Deleted", args=dict(deleted=True)), - grids.GridColumnFilter("All", args=dict(deleted="All")), - ] - num_rows_per_page = 50 - use_paging = True + + def apply_query_filter(self, query, **kwargs): + INDEX_SEARCH_FILTERS = { + "name": "name", + "is": "is", + } + deleted = False + search_query = kwargs.get("search") + if search_query: + parsed_search = parse_filters_structured(search_query, INDEX_SEARCH_FILTERS) + for term in parsed_search.terms: + if isinstance(term, FilteredTerm): + key = term.filter + q = term.text + if key == "name": + query = query.filter(text_column_filter(self.model_class.name, term)) + elif key == "is": + if q == "deleted": + deleted = True + elif isinstance(term, RawTextTerm): + query = query.filter( + raw_text_column_filter( + [ + self.model_class.name, + ], + term, + ) + ) + query = query.filter(self.model_class.deleted == (true() if deleted else false())) + return query class QuotaListGrid(grids.Grid): From 11e1e1c4d3119ff4ac013c0fd3571a7650ebf16e Mon Sep 17 00:00:00 2001 From: guerler Date: Sat, 2 Dec 2023 20:22:21 +0300 Subject: [PATCH 2/8] Move group operations from legacy admin controller to api endpoint, and link to client --- client/src/api/groups.ts | 6 +- client/src/api/schema/schema.ts | 90 +++++++++ .../components/Grid/configs/adminGroups.ts | 176 ++++++++++++++++++ lib/galaxy/managers/groups.py | 33 ++++ lib/galaxy/webapps/galaxy/api/groups.py | 15 ++ .../webapps/galaxy/controllers/admin.py | 60 ------ 6 files changed, 319 insertions(+), 61 deletions(-) create mode 100644 client/src/components/Grid/configs/adminGroups.ts diff --git a/client/src/api/groups.ts b/client/src/api/groups.ts index 4f0c3e236e87..2365bcc838e0 100644 --- a/client/src/api/groups.ts +++ b/client/src/api/groups.ts @@ -1,9 +1,13 @@ import axios from "axios"; -import { components } from "@/api/schema"; +import { components, fetcher } from "@/api/schema"; type GroupModel = components["schemas"]["GroupModel"]; export async function getAllGroups(): Promise { const { data } = await axios.get("/api/groups"); return data; } + +export const deleteGroup = fetcher.path("/api/groups/{id}").method("delete").create(); +export const purgeGroup = fetcher.path("/api/groups/{id}/purge").method("post").create(); +export const undeleteGroup = fetcher.path("/api/groups/{id}/undelete").method("post").create(); diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index cb551a3c25e3..18e4fa72eaa3 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -451,6 +451,18 @@ export interface paths { */ delete: operations["delete_api_groups__group_id__users__user_id__delete"]; }; + "/api/groups/{id}": { + /** Delete */ + delete: operations["delete_api_groups__id__delete"]; + }; + "/api/groups/{id}/purge": { + /** Purge */ + post: operations["purge_api_groups__id__purge_post"]; + }; + "/api/groups/{id}/undelete": { + /** Undelete */ + post: operations["undelete_api_groups__id__undelete_post"]; + }; "/api/help/forum/search": { /** * Search the Galaxy Help forum. @@ -12385,6 +12397,84 @@ export interface operations { }; }; }; + delete_api_groups__id__delete: { + /** Delete */ + parameters: { + /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ + header?: { + "run-as"?: string; + }; + path: { + id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": Record; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + purge_api_groups__id__purge_post: { + /** Purge */ + parameters: { + /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ + header?: { + "run-as"?: string; + }; + path: { + id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": Record; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + undelete_api_groups__id__undelete_post: { + /** Undelete */ + parameters: { + /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ + header?: { + "run-as"?: string; + }; + path: { + id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": Record; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; search_forum_api_help_forum_search_get: { /** * Search the Galaxy Help forum. diff --git a/client/src/components/Grid/configs/adminGroups.ts b/client/src/components/Grid/configs/adminGroups.ts new file mode 100644 index 000000000000..1c97fddff426 --- /dev/null +++ b/client/src/components/Grid/configs/adminGroups.ts @@ -0,0 +1,176 @@ +import { faEdit, faKey, faPlus, faTrash, faTrashRestore } from "@fortawesome/free-solid-svg-icons"; +import { useEventBus } from "@vueuse/core"; +import axios from "axios"; + +import { deleteGroup, purgeGroup, undeleteGroup } from "@/api/groups"; +import Filtering, { contains, equals, toBool, type ValidFilter } from "@/utils/filtering"; +import { withPrefix } from "@/utils/redirect"; +import { errorMessageAsString } from "@/utils/simple-error"; + +import type { ActionArray, FieldArray, GridConfig } from "./types"; + +const { emit } = useEventBus("grid-router-push"); + +/** + * Local types + */ +type GroupEntry = Record; + +/** + * Request and return data from server + */ +async function getData(offset: number, limit: number, search: string, sort_by: string, sort_desc: boolean) { + const query = { + limit: String(limit), + offset: String(offset), + search: search, + sort_by: sort_by, + sort_desc: String(sort_desc), + }; + const queryString = new URLSearchParams(query).toString(); + const { data } = await axios.get(withPrefix(`/admin/groups_list?${queryString}`)); + return [data.rows, data.rows_total]; +} + +/** + * Actions are grid-wide operations + */ +const actions: ActionArray = [ + { + title: "Create New Group", + icon: faPlus, + handler: () => { + emit("/admin/form/create_group"); + }, + }, +]; + +/** + * Declare columns to be displayed + */ +const fields: FieldArray = [ + { + key: "name", + title: "Name", + type: "operations", + operations: [ + { + title: "Edit Name/Description", + icon: faEdit, + condition: (data: GroupEntry) => !data.deleted, + handler: (data: GroupEntry) => { + emit(`/admin/form/rename_group?id=${data.id}`); + }, + }, + { + title: "Edit Permissions", + icon: faKey, + condition: (data: GroupEntry) => !data.deleted, + handler: (data: GroupEntry) => { + emit(`/admin/form/manage_users_and_groups_for_role?id=${data.id}`); + }, + }, + { + title: "Delete", + icon: faTrash, + condition: (data: GroupEntry) => !data.deleted, + handler: async (data: GroupEntry) => { + try { + await deleteGroup({ id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been deleted.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`, + }; + } + }, + }, + { + title: "Purge", + icon: faTrash, + condition: (data: GroupEntry) => !!data.deleted, + handler: async (data: GroupEntry) => { + try { + await purgeGroup({ id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been purged.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to purge '${data.name}': ${errorMessageAsString(e)}`, + }; + } + }, + }, + { + title: "Restore", + icon: faTrashRestore, + condition: (data: GroupEntry) => !!data.deleted, + handler: async (data: GroupEntry) => { + try { + await undeleteGroup({ id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been restored.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to restore '${data.name}': ${errorMessageAsString(e)}`, + }; + } + }, + }, + ], + }, + { + key: "roles", + title: "Roles", + type: "text", + }, + { + key: "users", + title: "Users", + type: "text", + }, + { + key: "update_time", + title: "Updated", + type: "date", + }, +]; + +const validFilters: Record> = { + name: { placeholder: "name", type: String, handler: contains("name"), menuItem: true }, + deleted: { + placeholder: "Filter on deleted entries", + type: Boolean, + boolType: "is", + handler: equals("deleted", "deleted", toBool), + menuItem: true, + }, +}; + +/** + * Grid configuration + */ +const gridConfig: GridConfig = { + id: "groups-grid", + actions: actions, + fields: fields, + filtering: new Filtering(validFilters, undefined, false, false), + getData: getData, + plural: "Groups", + sortBy: "name", + sortDesc: true, + sortKeys: ["name", "update_time"], + title: "Groups", +}; + +export default gridConfig; diff --git a/lib/galaxy/managers/groups.py b/lib/galaxy/managers/groups.py index e8919d4db72a..270348e44c86 100644 --- a/lib/galaxy/managers/groups.py +++ b/lib/galaxy/managers/groups.py @@ -101,6 +101,39 @@ def update(self, trans: ProvidesAppContext, group_id: int, payload: GroupCreateP item["url"] = self._url_for(trans, "show_group", group_id=encoded_id) return item + def delete(self, trans: ProvidesAppContext, group_id: int): + group = self._get_group(trans.sa_session, group_id) + group.deleted = True + trans.sa_session.add(group) + with transaction(trans.sa_session): + trans.sa_session.commit() + + def purge(self, trans: ProvidesAppContext, group_id: int): + group = self._get_group(trans.sa_session, group_id) + if not group.deleted: + raise galaxy.exceptions.RequestParameterInvalidException( + f"Group '{groups.name}' has not been deleted, so it cannot be purged." + ) + # Delete UserGroupAssociations + for uga in group.users: + trans.sa_session.delete(uga) + # Delete GroupRoleAssociations + for gra in group.roles: + trans.sa_session.delete(gra) + with transaction(trans.sa_session): + trans.sa_session.commit() + + def undelete(self, trans: ProvidesAppContext, group_id: int): + group = self._get_group(trans.sa_session, group_id) + if not group.deleted: + raise galaxy.exceptions.RequestParameterInvalidException( + f"Group '{groups.name}' has not been deleted, so it cannot be undeleted." + ) + group.deleted = False + trans.sa_session.add(group) + with transaction(trans.sa_session): + trans.sa_session.commit() + def _url_for(self, trans, name, **kwargs): return trans.url_builder(name, **kwargs) diff --git a/lib/galaxy/webapps/galaxy/api/groups.py b/lib/galaxy/webapps/galaxy/api/groups.py index 2b4c7e1ca1d5..9b6b88ff6546 100644 --- a/lib/galaxy/webapps/galaxy/api/groups.py +++ b/lib/galaxy/webapps/galaxy/api/groups.py @@ -82,3 +82,18 @@ def update( payload: GroupCreatePayload = Body(...), ) -> GroupResponse: return self.manager.update(trans, group_id, payload) + + @router.delete("/api/groups/{id}", require_admin=True) + def delete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + group = self.manager.get(trans, id) + self.manager.delete(trans, group) + + @router.post("/api/groups/{id}/purge", require_admin=True) + def purge(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + group = self.manager.get(trans, id) + self.manager.purge(trans, group) + + @router.post("/api/groups/{id}/undelete", require_admin=True) + def undelete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + group = self.manager.get(trans, id) + self.manager.undelete(trans, group) diff --git a/lib/galaxy/webapps/galaxy/controllers/admin.py b/lib/galaxy/webapps/galaxy/controllers/admin.py index a6239cf46c03..33d0875563b7 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin.py @@ -1002,23 +1002,6 @@ def manage_users_and_groups_for_role(self, trans, payload=None, **kwd): @web.legacy_expose_api @web.require_admin def groups_list(self, trans, **kwargs): - message = kwargs.get("message") - status = kwargs.get("status") - if "operation" in kwargs: - id = kwargs.get("id") - if not id: - return self.message_exception(trans, f"Invalid group id ({str(id)}) received.") - ids = util.listify(id) - operation = kwargs["operation"].lower().replace("+", " ") - if operation == "delete": - message, status = self._delete_group(trans, ids) - elif operation == "undelete": - message, status = self._undelete_group(trans, ids) - elif operation == "purge": - message, status = self._purge_group(trans, ids) - if message and status: - kwargs["message"] = util.sanitize_text(message) - kwargs["status"] = status return self.group_list_grid(trans, **kwargs) @web.legacy_expose_api @@ -1201,49 +1184,6 @@ def create_group(self, trans, payload=None, **kwd): ) return {"message": message} - def _delete_group(self, trans, ids): - message = "Deleted %d groups: " % len(ids) - for group_id in ids: - group = get_group(trans, group_id) - group.deleted = True - trans.sa_session.add(group) - with transaction(trans.sa_session): - trans.sa_session.commit() - message += f" {group.name} " - return (message, "done") - - def _undelete_group(self, trans, ids): - count = 0 - undeleted_groups = "" - for group_id in ids: - group = get_group(trans, group_id) - if not group.deleted: - return (f"Group '{group.name}' has not been deleted, so it cannot be undeleted.", "error") - group.deleted = False - trans.sa_session.add(group) - with transaction(trans.sa_session): - trans.sa_session.commit() - count += 1 - undeleted_groups += f" {group.name}" - return ("Undeleted %d groups: %s" % (count, undeleted_groups), "done") - - def _purge_group(self, trans, ids): - message = "Purged %d groups: " % len(ids) - for group_id in ids: - group = get_group(trans, group_id) - if not group.deleted: - return (f"Group '{group.name}' has not been deleted, so it cannot be purged.", "error") - # Delete UserGroupAssociations - for uga in group.users: - trans.sa_session.delete(uga) - # Delete GroupRoleAssociations - for gra in group.roles: - trans.sa_session.delete(gra) - with transaction(trans.sa_session): - trans.sa_session.commit() - message += f" {group.name} " - return (message, "done") - @web.expose @web.require_admin def create_new_user(self, trans, **kwd): From 9b85946b6de4dacfb453de639387d195d33f234f Mon Sep 17 00:00:00 2001 From: guerler Date: Sat, 2 Dec 2023 20:43:19 +0300 Subject: [PATCH 3/8] Replace legacy grid with grid list component in client router --- client/src/components/Grid/configs/adminGroups.ts | 4 ++-- client/src/entry/analysis/routes/admin-routes.js | 5 +++-- lib/galaxy/managers/groups.py | 9 +++++---- lib/galaxy/webapps/galaxy/api/groups.py | 9 +++------ lib/galaxy/webapps/galaxy/controllers/admin.py | 2 +- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client/src/components/Grid/configs/adminGroups.ts b/client/src/components/Grid/configs/adminGroups.ts index 1c97fddff426..6c3d07c4eb6f 100644 --- a/client/src/components/Grid/configs/adminGroups.ts +++ b/client/src/components/Grid/configs/adminGroups.ts @@ -55,7 +55,7 @@ const fields: FieldArray = [ type: "operations", operations: [ { - title: "Edit Name/Description", + title: "Edit Name", icon: faEdit, condition: (data: GroupEntry) => !data.deleted, handler: (data: GroupEntry) => { @@ -67,7 +67,7 @@ const fields: FieldArray = [ icon: faKey, condition: (data: GroupEntry) => !data.deleted, handler: (data: GroupEntry) => { - emit(`/admin/form/manage_users_and_groups_for_role?id=${data.id}`); + emit(`/admin/form/manage_users_and_roles_for_group?id=${data.id}`); }, }, { diff --git a/client/src/entry/analysis/routes/admin-routes.js b/client/src/entry/analysis/routes/admin-routes.js index 7be38abcfc62..864a8f3b363a 100644 --- a/client/src/entry/analysis/routes/admin-routes.js +++ b/client/src/entry/analysis/routes/admin-routes.js @@ -18,6 +18,7 @@ import NotificationsManagement from "components/admin/Notifications/Notification import ResetMetadata from "components/admin/ResetMetadata"; import SanitizeAllow from "components/admin/SanitizeAllow"; import FormGeneric from "components/Form/FormGeneric"; +import adminGroupsGridConfig from "components/Grid/configs/adminGroups"; import adminRolesGridConfig from "components/Grid/configs/adminRoles"; import adminUsersGridConfig from "components/Grid/configs/adminUsers"; import Grid from "components/Grid/Grid"; @@ -135,9 +136,9 @@ export default [ }, { path: "groups", - component: Grid, + component: GridList, props: { - urlBase: "admin/groups_list", + gridConfig: adminGroupsGridConfig, }, }, { diff --git a/lib/galaxy/managers/groups.py b/lib/galaxy/managers/groups.py index 270348e44c86..6cbea985d74a 100644 --- a/lib/galaxy/managers/groups.py +++ b/lib/galaxy/managers/groups.py @@ -9,6 +9,7 @@ Conflict, ObjectAttributeMissingException, ObjectNotFound, + RequestParameterInvalidException, ) from galaxy.managers.context import ProvidesAppContext from galaxy.managers.roles import get_roles_by_ids @@ -111,8 +112,8 @@ def delete(self, trans: ProvidesAppContext, group_id: int): def purge(self, trans: ProvidesAppContext, group_id: int): group = self._get_group(trans.sa_session, group_id) if not group.deleted: - raise galaxy.exceptions.RequestParameterInvalidException( - f"Group '{groups.name}' has not been deleted, so it cannot be purged." + raise RequestParameterInvalidException( + f"Group '{group.name}' has not been deleted, so it cannot be purged." ) # Delete UserGroupAssociations for uga in group.users: @@ -126,8 +127,8 @@ def purge(self, trans: ProvidesAppContext, group_id: int): def undelete(self, trans: ProvidesAppContext, group_id: int): group = self._get_group(trans.sa_session, group_id) if not group.deleted: - raise galaxy.exceptions.RequestParameterInvalidException( - f"Group '{groups.name}' has not been deleted, so it cannot be undeleted." + raise RequestParameterInvalidException( + f"Group '{group.name}' has not been deleted, so it cannot be undeleted." ) group.deleted = False trans.sa_session.add(group) diff --git a/lib/galaxy/webapps/galaxy/api/groups.py b/lib/galaxy/webapps/galaxy/api/groups.py index 9b6b88ff6546..06aae8d4970f 100644 --- a/lib/galaxy/webapps/galaxy/api/groups.py +++ b/lib/galaxy/webapps/galaxy/api/groups.py @@ -85,15 +85,12 @@ def update( @router.delete("/api/groups/{id}", require_admin=True) def delete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - group = self.manager.get(trans, id) - self.manager.delete(trans, group) + self.manager.delete(trans, id) @router.post("/api/groups/{id}/purge", require_admin=True) def purge(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - group = self.manager.get(trans, id) - self.manager.purge(trans, group) + self.manager.purge(trans, id) @router.post("/api/groups/{id}/undelete", require_admin=True) def undelete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - group = self.manager.get(trans, id) - self.manager.undelete(trans, group) + self.manager.undelete(trans, id) diff --git a/lib/galaxy/webapps/galaxy/controllers/admin.py b/lib/galaxy/webapps/galaxy/controllers/admin.py index 33d0875563b7..73994f0a8297 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin.py @@ -247,7 +247,7 @@ def get_value(self, trans, grid, group): UsersColumn("Users", key="users"), RolesColumn("Roles", key="roles"), grids.DeletedColumn("Deleted", key="deleted", escape=False), - grids.GridColumn("Last Updated", key="update_time", format=pretty_print_time_interval), + grids.GridColumn("Last Updated", key="update_time"), ] def apply_query_filter(self, query, **kwargs): From cd5087be830b3d828f19d94afe556e4f8e2ac53d Mon Sep 17 00:00:00 2001 From: guerler Date: Sun, 3 Dec 2023 08:26:21 +0300 Subject: [PATCH 4/8] Use groups_id instead of id in groups api for consistency --- client/src/api/groups.ts | 6 +- client/src/api/schema/schema.ts | 172 +++++++++--------- .../components/Grid/configs/adminGroups.ts | 6 +- lib/galaxy/webapps/galaxy/api/groups.py | 12 +- .../webapps/galaxy/controllers/admin.py | 1 - 5 files changed, 97 insertions(+), 100 deletions(-) diff --git a/client/src/api/groups.ts b/client/src/api/groups.ts index 2365bcc838e0..e795ccfdbf47 100644 --- a/client/src/api/groups.ts +++ b/client/src/api/groups.ts @@ -8,6 +8,6 @@ export async function getAllGroups(): Promise { return data; } -export const deleteGroup = fetcher.path("/api/groups/{id}").method("delete").create(); -export const purgeGroup = fetcher.path("/api/groups/{id}/purge").method("post").create(); -export const undeleteGroup = fetcher.path("/api/groups/{id}/undelete").method("post").create(); +export const deleteGroup = fetcher.path("/api/groups/{group_id}").method("delete").create(); +export const purgeGroup = fetcher.path("/api/groups/{group_id}/purge").method("post").create(); +export const undeleteGroup = fetcher.path("/api/groups/{group_id}/undelete").method("post").create(); diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 18e4fa72eaa3..a1a33e792bf4 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -392,6 +392,12 @@ export interface paths { get: operations["show_group_api_groups__group_id__get"]; /** Modifies a group. */ put: operations["update_api_groups__group_id__put"]; + /** Delete */ + delete: operations["delete_api_groups__group_id__delete"]; + }; + "/api/groups/{group_id}/purge": { + /** Purge */ + post: operations["purge_api_groups__group_id__purge_post"]; }; "/api/groups/{group_id}/roles": { /** Displays a collection (list) of groups. */ @@ -405,6 +411,10 @@ export interface paths { /** Removes a role from a group */ delete: operations["delete_api_groups__group_id__roles__role_id__delete"]; }; + "/api/groups/{group_id}/undelete": { + /** Undelete */ + post: operations["undelete_api_groups__group_id__undelete_post"]; + }; "/api/groups/{group_id}/user/{user_id}": { /** * Displays information about a group user. @@ -451,18 +461,6 @@ export interface paths { */ delete: operations["delete_api_groups__group_id__users__user_id__delete"]; }; - "/api/groups/{id}": { - /** Delete */ - delete: operations["delete_api_groups__id__delete"]; - }; - "/api/groups/{id}/purge": { - /** Purge */ - post: operations["purge_api_groups__id__purge_post"]; - }; - "/api/groups/{id}/undelete": { - /** Undelete */ - post: operations["undelete_api_groups__id__undelete_post"]; - }; "/api/help/forum/search": { /** * Search the Galaxy Help forum. @@ -12056,14 +12054,13 @@ export interface operations { }; }; }; - group_roles_api_groups__group_id__roles_get: { - /** Displays a collection (list) of groups. */ + delete_api_groups__group_id__delete: { + /** Delete */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; - /** @description The ID of the group */ path: { group_id: string; }; @@ -12072,7 +12069,7 @@ export interface operations { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupRoleListResponse"]; + "application/json": Record; }; }; /** @description Validation Error */ @@ -12083,25 +12080,22 @@ export interface operations { }; }; }; - group_role_api_groups__group_id__roles__role_id__get: { - /** Displays information about a group role. */ + purge_api_groups__group_id__purge_post: { + /** Purge */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; - /** @description The ID of the group */ - /** @description The ID of the role */ path: { group_id: string; - role_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupRoleResponse"]; + "application/json": Record; }; }; /** @description Validation Error */ @@ -12112,25 +12106,23 @@ export interface operations { }; }; }; - update_api_groups__group_id__roles__role_id__put: { - /** Adds a role to a group */ + group_roles_api_groups__group_id__roles_get: { + /** Displays a collection (list) of groups. */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; /** @description The ID of the group */ - /** @description The ID of the role */ path: { group_id: string; - role_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupRoleResponse"]; + "application/json": components["schemas"]["GroupRoleListResponse"]; }; }; /** @description Validation Error */ @@ -12141,8 +12133,8 @@ export interface operations { }; }; }; - delete_api_groups__group_id__roles__role_id__delete: { - /** Removes a role from a group */ + group_role_api_groups__group_id__roles__role_id__get: { + /** Displays information about a group role. */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { @@ -12170,28 +12162,25 @@ export interface operations { }; }; }; - group_user_api_groups__group_id__user__user_id__get: { - /** - * Displays information about a group user. - * @description Displays information about a group user. - */ + update_api_groups__group_id__roles__role_id__put: { + /** Adds a role to a group */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; /** @description The ID of the group */ - /** @description The ID of the user */ + /** @description The ID of the role */ path: { group_id: string; - user_id: string; + role_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupUserResponse"]; + "application/json": components["schemas"]["GroupRoleResponse"]; }; }; /** @description Validation Error */ @@ -12202,29 +12191,25 @@ export interface operations { }; }; }; - update_api_groups__group_id__user__user_id__put: { - /** - * Adds a user to a group - * @description PUT /api/groups/{encoded_group_id}/users/{encoded_user_id} - * Adds a user to a group - */ + delete_api_groups__group_id__roles__role_id__delete: { + /** Removes a role from a group */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; /** @description The ID of the group */ - /** @description The ID of the user */ + /** @description The ID of the role */ path: { group_id: string; - user_id: string; + role_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupUserResponse"]; + "application/json": components["schemas"]["GroupRoleResponse"]; }; }; /** @description Validation Error */ @@ -12235,29 +12220,22 @@ export interface operations { }; }; }; - delete_api_groups__group_id__user__user_id__delete: { - /** - * Removes a user from a group - * @description DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id} - * Removes a user from a group - */ + undelete_api_groups__group_id__undelete_post: { + /** Undelete */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; - /** @description The ID of the group */ - /** @description The ID of the user */ path: { group_id: string; - user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupUserResponse"]; + "application/json": Record; }; }; /** @description Validation Error */ @@ -12268,11 +12246,10 @@ export interface operations { }; }; }; - group_users_api_groups__group_id__users_get: { + group_user_api_groups__group_id__user__user_id__get: { /** - * Displays a collection (list) of groups. - * @description GET /api/groups/{encoded_group_id}/users - * Displays a collection (list) of groups. + * Displays information about a group user. + * @description Displays information about a group user. */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ @@ -12280,15 +12257,17 @@ export interface operations { "run-as"?: string; }; /** @description The ID of the group */ + /** @description The ID of the user */ path: { group_id: string; + user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupUserListResponse"]; + "application/json": components["schemas"]["GroupUserResponse"]; }; }; /** @description Validation Error */ @@ -12299,10 +12278,11 @@ export interface operations { }; }; }; - group_user_api_groups__group_id__users__user_id__get: { + update_api_groups__group_id__user__user_id__put: { /** - * Displays information about a group user. - * @description Displays information about a group user. + * Adds a user to a group + * @description PUT /api/groups/{encoded_group_id}/users/{encoded_user_id} + * Adds a user to a group */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ @@ -12331,11 +12311,11 @@ export interface operations { }; }; }; - update_api_groups__group_id__users__user_id__put: { + delete_api_groups__group_id__user__user_id__delete: { /** - * Adds a user to a group - * @description PUT /api/groups/{encoded_group_id}/users/{encoded_user_id} - * Adds a user to a group + * Removes a user from a group + * @description DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id} + * Removes a user from a group */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ @@ -12364,11 +12344,11 @@ export interface operations { }; }; }; - delete_api_groups__group_id__users__user_id__delete: { + group_users_api_groups__group_id__users_get: { /** - * Removes a user from a group - * @description DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id} - * Removes a user from a group + * Displays a collection (list) of groups. + * @description GET /api/groups/{encoded_group_id}/users + * Displays a collection (list) of groups. */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ @@ -12376,17 +12356,15 @@ export interface operations { "run-as"?: string; }; /** @description The ID of the group */ - /** @description The ID of the user */ path: { group_id: string; - user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["GroupUserResponse"]; + "application/json": components["schemas"]["GroupUserListResponse"]; }; }; /** @description Validation Error */ @@ -12397,22 +12375,28 @@ export interface operations { }; }; }; - delete_api_groups__id__delete: { - /** Delete */ + group_user_api_groups__group_id__users__user_id__get: { + /** + * Displays information about a group user. + * @description Displays information about a group user. + */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; + /** @description The ID of the group */ + /** @description The ID of the user */ path: { - id: string; + group_id: string; + user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": Record; + "application/json": components["schemas"]["GroupUserResponse"]; }; }; /** @description Validation Error */ @@ -12423,22 +12407,29 @@ export interface operations { }; }; }; - purge_api_groups__id__purge_post: { - /** Purge */ + update_api_groups__group_id__users__user_id__put: { + /** + * Adds a user to a group + * @description PUT /api/groups/{encoded_group_id}/users/{encoded_user_id} + * Adds a user to a group + */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; + /** @description The ID of the group */ + /** @description The ID of the user */ path: { - id: string; + group_id: string; + user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": Record; + "application/json": components["schemas"]["GroupUserResponse"]; }; }; /** @description Validation Error */ @@ -12449,22 +12440,29 @@ export interface operations { }; }; }; - undelete_api_groups__id__undelete_post: { - /** Undelete */ + delete_api_groups__group_id__users__user_id__delete: { + /** + * Removes a user from a group + * @description DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id} + * Removes a user from a group + */ parameters: { /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { "run-as"?: string; }; + /** @description The ID of the group */ + /** @description The ID of the user */ path: { - id: string; + group_id: string; + user_id: string; }; }; responses: { /** @description Successful Response */ 200: { content: { - "application/json": Record; + "application/json": components["schemas"]["GroupUserResponse"]; }; }; /** @description Validation Error */ diff --git a/client/src/components/Grid/configs/adminGroups.ts b/client/src/components/Grid/configs/adminGroups.ts index 6c3d07c4eb6f..cf3c666eb86c 100644 --- a/client/src/components/Grid/configs/adminGroups.ts +++ b/client/src/components/Grid/configs/adminGroups.ts @@ -76,7 +76,7 @@ const fields: FieldArray = [ condition: (data: GroupEntry) => !data.deleted, handler: async (data: GroupEntry) => { try { - await deleteGroup({ id: String(data.id) }); + await deleteGroup({ group_id: String(data.id) }); return { status: "success", message: `'${data.name}' has been deleted.`, @@ -95,7 +95,7 @@ const fields: FieldArray = [ condition: (data: GroupEntry) => !!data.deleted, handler: async (data: GroupEntry) => { try { - await purgeGroup({ id: String(data.id) }); + await purgeGroup({ group_id: String(data.id) }); return { status: "success", message: `'${data.name}' has been purged.`, @@ -114,7 +114,7 @@ const fields: FieldArray = [ condition: (data: GroupEntry) => !!data.deleted, handler: async (data: GroupEntry) => { try { - await undeleteGroup({ id: String(data.id) }); + await undeleteGroup({ group_id: String(data.id) }); return { status: "success", message: `'${data.name}' has been restored.`, diff --git a/lib/galaxy/webapps/galaxy/api/groups.py b/lib/galaxy/webapps/galaxy/api/groups.py index 06aae8d4970f..9c9eb7657866 100644 --- a/lib/galaxy/webapps/galaxy/api/groups.py +++ b/lib/galaxy/webapps/galaxy/api/groups.py @@ -83,14 +83,14 @@ def update( ) -> GroupResponse: return self.manager.update(trans, group_id, payload) - @router.delete("/api/groups/{id}", require_admin=True) - def delete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + @router.delete("/api/groups/{group_id}", require_admin=True) + def delete(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): self.manager.delete(trans, id) - @router.post("/api/groups/{id}/purge", require_admin=True) - def purge(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + @router.post("/api/groups/{group_id}/purge", require_admin=True) + def purge(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): self.manager.purge(trans, id) - @router.post("/api/groups/{id}/undelete", require_admin=True) - def undelete(self, id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): + @router.post("/api/groups/{group_id}/undelete", require_admin=True) + def undelete(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): self.manager.undelete(trans, id) diff --git a/lib/galaxy/webapps/galaxy/controllers/admin.py b/lib/galaxy/webapps/galaxy/controllers/admin.py index 73994f0a8297..ac2109d310bc 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin.py @@ -26,7 +26,6 @@ ) from galaxy.security.validate_user_input import validate_password from galaxy.structured_app import StructuredApp -from galaxy.util import pretty_print_time_interval from galaxy.util.search import ( FilteredTerm, parse_filters_structured, From 332177b4b76997af716f8258115cc08d99743419 Mon Sep 17 00:00:00 2001 From: guerler Date: Sun, 3 Dec 2023 08:41:23 +0300 Subject: [PATCH 5/8] Adjust groups grid selenium test action button selector --- client/src/utils/navigation/navigation.yml | 2 +- lib/galaxy/webapps/galaxy/api/groups.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/utils/navigation/navigation.yml b/client/src/utils/navigation/navigation.yml index e1dba3b8c8ca..1eb623ea1869 100644 --- a/client/src/utils/navigation/navigation.yml +++ b/client/src/utils/navigation/navigation.yml @@ -956,7 +956,7 @@ admin: dm_table_card: '#data-table-card' users_grid: '#users-grid' users_grid_create_button: '[data-description="grid action create new user"]' - groups_grid_create_button: '.manage-table-actions .action-button' + groups_grid_create_button: '[data-description="grid action create new group"]' registration_form: 'form#registration' groups_grid: '#groups-grid' roles_grid: '#roles-grid' diff --git a/lib/galaxy/webapps/galaxy/api/groups.py b/lib/galaxy/webapps/galaxy/api/groups.py index 9c9eb7657866..c571983df48a 100644 --- a/lib/galaxy/webapps/galaxy/api/groups.py +++ b/lib/galaxy/webapps/galaxy/api/groups.py @@ -85,12 +85,12 @@ def update( @router.delete("/api/groups/{group_id}", require_admin=True) def delete(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - self.manager.delete(trans, id) + self.manager.delete(trans, group_id) @router.post("/api/groups/{group_id}/purge", require_admin=True) def purge(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - self.manager.purge(trans, id) + self.manager.purge(trans, group_id) @router.post("/api/groups/{group_id}/undelete", require_admin=True) def undelete(self, group_id: DecodedDatabaseIdField, trans: ProvidesAppContext = DependsOnTrans): - self.manager.undelete(trans, id) + self.manager.undelete(trans, group_id) From b49f8e2b52c53fb8f80cf25c9ec0b34b84ba739e Mon Sep 17 00:00:00 2001 From: guerler Date: Thu, 7 Dec 2023 13:21:35 +0300 Subject: [PATCH 6/8] Add confirmation alert for delete and purge operations in role and group grids, should be replace by a vue-modal in follow-ups --- .../components/Grid/configs/adminGroups.ts | 49 ++++++++++--------- .../src/components/Grid/configs/adminRoles.ts | 49 ++++++++++--------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/client/src/components/Grid/configs/adminGroups.ts b/client/src/components/Grid/configs/adminGroups.ts index cf3c666eb86c..495d73f2fb42 100644 --- a/client/src/components/Grid/configs/adminGroups.ts +++ b/client/src/components/Grid/configs/adminGroups.ts @@ -4,6 +4,7 @@ import axios from "axios"; import { deleteGroup, purgeGroup, undeleteGroup } from "@/api/groups"; import Filtering, { contains, equals, toBool, type ValidFilter } from "@/utils/filtering"; +import _l from "@/utils/localization"; import { withPrefix } from "@/utils/redirect"; import { errorMessageAsString } from "@/utils/simple-error"; @@ -75,17 +76,19 @@ const fields: FieldArray = [ icon: faTrash, condition: (data: GroupEntry) => !data.deleted, handler: async (data: GroupEntry) => { - try { - await deleteGroup({ group_id: String(data.id) }); - return { - status: "success", - message: `'${data.name}' has been deleted.`, - }; - } catch (e) { - return { - status: "danger", - message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`, - }; + if (confirm(_l("Are you sure that you want to delete this group?"))) { + try { + await deleteGroup({ group_id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been deleted.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`, + }; + } } }, }, @@ -94,17 +97,19 @@ const fields: FieldArray = [ icon: faTrash, condition: (data: GroupEntry) => !!data.deleted, handler: async (data: GroupEntry) => { - try { - await purgeGroup({ group_id: String(data.id) }); - return { - status: "success", - message: `'${data.name}' has been purged.`, - }; - } catch (e) { - return { - status: "danger", - message: `Failed to purge '${data.name}': ${errorMessageAsString(e)}`, - }; + if (confirm(_l("Are you sure that you want to purge this group?"))) { + try { + await purgeGroup({ group_id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been purged.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to purge '${data.name}': ${errorMessageAsString(e)}`, + }; + } } }, }, diff --git a/client/src/components/Grid/configs/adminRoles.ts b/client/src/components/Grid/configs/adminRoles.ts index b1365e27b7c9..f1f08012c326 100644 --- a/client/src/components/Grid/configs/adminRoles.ts +++ b/client/src/components/Grid/configs/adminRoles.ts @@ -4,6 +4,7 @@ import axios from "axios"; import { deleteRole, purgeRole, undeleteRole } from "@/api/roles"; import Filtering, { contains, equals, toBool, type ValidFilter } from "@/utils/filtering"; +import _l from "@/utils/localization"; import { withPrefix } from "@/utils/redirect"; import { errorMessageAsString } from "@/utils/simple-error"; @@ -75,17 +76,19 @@ const fields: FieldArray = [ icon: faTrash, condition: (data: RoleEntry) => !data.deleted, handler: async (data: RoleEntry) => { - try { - await deleteRole({ id: String(data.id) }); - return { - status: "success", - message: `'${data.name}' has been deleted.`, - }; - } catch (e) { - return { - status: "danger", - message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`, - }; + if (confirm(_l("Are you sure that you want to delete this role?"))) { + try { + await deleteRole({ id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been deleted.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`, + }; + } } }, }, @@ -94,17 +97,19 @@ const fields: FieldArray = [ icon: faTrash, condition: (data: RoleEntry) => !!data.deleted, handler: async (data: RoleEntry) => { - try { - await purgeRole({ id: String(data.id) }); - return { - status: "success", - message: `'${data.name}' has been purged.`, - }; - } catch (e) { - return { - status: "danger", - message: `Failed to purge '${data.name}': ${errorMessageAsString(e)}`, - }; + if (confirm(_l("Are you sure that you want to purge this role?"))) { + try { + await purgeRole({ id: String(data.id) }); + return { + status: "success", + message: `'${data.name}' has been purged.`, + }; + } catch (e) { + return { + status: "danger", + message: `Failed to purge '${data.name}': ${errorMessageAsString(e)}`, + }; + } } }, }, From b4cc8cee0c9a82d09027630b436ce5482e8fd0c4 Mon Sep 17 00:00:00 2001 From: guerler Date: Thu, 7 Dec 2023 13:23:12 +0300 Subject: [PATCH 7/8] Fix redirect from roles and groups user management form --- client/src/entry/analysis/routes/admin-routes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/entry/analysis/routes/admin-routes.js b/client/src/entry/analysis/routes/admin-routes.js index 864a8f3b363a..ac96c2ef4965 100644 --- a/client/src/entry/analysis/routes/admin-routes.js +++ b/client/src/entry/analysis/routes/admin-routes.js @@ -195,7 +195,7 @@ export default [ component: FormGeneric, props: (route) => ({ url: `/admin/manage_users_and_groups_for_role?id=${route.query.id}`, - redirect: "/admin/users", + redirect: "/admin/roles", }), }, { @@ -203,7 +203,7 @@ export default [ component: FormGeneric, props: (route) => ({ url: `/admin/manage_users_and_roles_for_group?id=${route.query.id}`, - redirect: "/admin/users", + redirect: "/admin/groups", }), }, { From 35bcefadc62e281f3ee68e2327fee75a573abd19 Mon Sep 17 00:00:00 2001 From: guerler Date: Thu, 7 Dec 2023 13:37:41 +0300 Subject: [PATCH 8/8] Display initial message parsed through route query in grid list component --- client/src/components/Grid/GridList.vue | 13 +++++++++++++ client/src/entry/analysis/routes/admin-routes.js | 15 +++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/client/src/components/Grid/GridList.vue b/client/src/components/Grid/GridList.vue index 357b728ca65b..c5de3f921608 100644 --- a/client/src/components/Grid/GridList.vue +++ b/client/src/components/Grid/GridList.vue @@ -27,6 +27,8 @@ library.add(faCaretDown, faCaretUp, faShieldAlt); interface Props { // provide a grid configuration gridConfig: GridConfig; + // incoming initial message + gridMessage?: string; // debounce delay delay?: number; // rows per page to be shown @@ -81,6 +83,16 @@ function applyFilter(filter: string, value: string | boolean, quoted = false) { } } +/** + * Display initial message parsed through route query + */ +function displayInitialMessage() { + if (props.gridMessage) { + operationMessage.value = props.gridMessage; + operationStatus.value = "success"; + } +} + /** * Request grid data */ @@ -164,6 +176,7 @@ function onFilter(filter?: string) { onMounted(() => { getGridData(); eventBus.on(onRouterPush); + displayInitialMessage(); }); onUnmounted(() => { diff --git a/client/src/entry/analysis/routes/admin-routes.js b/client/src/entry/analysis/routes/admin-routes.js index ac96c2ef4965..ad2e3437f9ca 100644 --- a/client/src/entry/analysis/routes/admin-routes.js +++ b/client/src/entry/analysis/routes/admin-routes.js @@ -137,9 +137,10 @@ export default [ { path: "groups", component: GridList, - props: { + props: (route) => ({ gridConfig: adminGroupsGridConfig, - }, + gridMessage: route.query.message, + }), }, { path: "quotas", @@ -151,16 +152,18 @@ export default [ { path: "roles", component: GridList, - props: { + props: (route) => ({ gridConfig: adminRolesGridConfig, - }, + gridMessage: route.query.message, + }), }, { path: "users", component: GridList, - props: { + props: (route) => ({ gridConfig: adminUsersGridConfig, - }, + gridMessage: route.query.message, + }), }, { path: "tool_versions",