From a739f88aa650ccb177ec79ecfcd227792ad8887e Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Tue, 17 Oct 2023 13:58:43 -0500 Subject: [PATCH] Restore deprecated ToolsApi and create new api route for new panel structure - restore `api/tools` that had the older (list of tool sections with `.elems`) structure - but use the new `api/tool_panel` structure in the client --- client/src/components/Panels/ToolBox.test.js | 4 +- .../ToolsSchemaJson/ToolsJson.test.js | 4 +- .../ToolsView/ToolsSchemaJson/ToolsJson.vue | 4 +- .../Toolshed/RepositoryDetails/Index.test.js | 2 +- client/src/store/index.js | 2 - client/src/store/panelStore.js | 66 ------------------- client/src/stores/toolStore.ts | 8 +-- lib/galaxy/webapps/galaxy/api/tools.py | 56 ++++++++++++++++ lib/galaxy/webapps/galaxy/buildapp.py | 1 + lib/galaxy_test/api/test_tools.py | 4 +- lib/galaxy_test/base/populators.py | 4 +- .../test/functional/test_galaxy_install.py | 4 +- test/integration/test_edam_toolbox.py | 4 +- test/integration/test_panel_views.py | 28 ++++---- 14 files changed, 90 insertions(+), 101 deletions(-) delete mode 100644 client/src/store/panelStore.js diff --git a/client/src/components/Panels/ToolBox.test.js b/client/src/components/Panels/ToolBox.test.js index c652b4ab1493..e8e14f029cf8 100644 --- a/client/src/components/Panels/ToolBox.test.js +++ b/client/src/components/Panels/ToolBox.test.js @@ -26,9 +26,9 @@ describe("ToolBox", () => { it("test filter functions correctly matching: (1) Tools store array-of-objects with (2) Results array", async () => { axiosMock - .onGet(`/api/tools`) + .onGet(`/api/tool_panel`) .replyOnce(200, toolsListInPanel) - .onGet(`/api/tools?in_panel=False`) + .onGet(`/api/tool_panel?in_panel=False`) .replyOnce(200, toolsMock) .onGet(/api\/tools?.*/) .replyOnce(200, resultsMock); diff --git a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js index 26e9423a497a..f798127e6711 100644 --- a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js +++ b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js @@ -18,8 +18,8 @@ describe("ToolSchemaJson/ToolsView.vue", () => { beforeEach(async () => { axiosMock = new MockAdapter(axios); - axiosMock.onGet("/api/tools?in_panel=False&tool_help=True").reply(200, testToolsListResponse); - axiosMock.onGet("/api/tools").reply(200, testToolsListInPanelResponse); + axiosMock.onGet("/api/tool_panel?in_panel=False&tool_help=True").reply(200, testToolsListResponse); + axiosMock.onGet("/api/tool_panel").reply(200, testToolsListInPanelResponse); wrapper = shallowMount(ToolsJson, { localVue }); await flushPromises(); }); diff --git a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.vue b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.vue index 300257bd2857..e5e2db7d1fbe 100644 --- a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.vue +++ b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.vue @@ -13,7 +13,7 @@ export default { async created() { let tools = {}; await axios - .get(`${getAppRoot()}api/tools?in_panel=False&tool_help=True`) + .get(`${getAppRoot()}api/tool_panel?in_panel=False&tool_help=True`) .then(({ data }) => { tools = data.tools; }) @@ -22,7 +22,7 @@ export default { }); if (Object.keys(tools).length > 0) { await axios - .get(`${getAppRoot()}api/tools`) + .get(`${getAppRoot()}api/tool_panel`) .then(({ data }) => { this.schemaTagObj = this.createToolsJson(tools, data.default); const el = document.createElement("script"); diff --git a/client/src/components/Toolshed/RepositoryDetails/Index.test.js b/client/src/components/Toolshed/RepositoryDetails/Index.test.js index dbcb10ea2741..2a3965db240d 100644 --- a/client/src/components/Toolshed/RepositoryDetails/Index.test.js +++ b/client/src/components/Toolshed/RepositoryDetails/Index.test.js @@ -34,7 +34,7 @@ Services.mockImplementation(() => { describe("RepositoryDetails", () => { it("test repository details index", async () => { const axiosMock = new MockAdapter(axios); - axiosMock.onGet("api/tools?in_panel=true&view=default").reply(200, {}); + axiosMock.onGet("api/tool_panel?in_panel=true&view=default").reply(200, {}); mockFetcher.path("/api/configuration").method("get").mock({ data: {} }); const localVue = getLocalVue(); const pinia = createPinia(); diff --git a/client/src/store/index.js b/client/src/store/index.js index 423886bc0d12..8ce43f061f30 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -14,7 +14,6 @@ import { datasetPathDestinationStore } from "./datasetPathDestinationStore"; import { gridSearchStore } from "./gridSearchStore"; import { invocationStore } from "./invocationStore"; import { jobDestinationParametersStore } from "./jobDestinationParametersStore"; -import { panelStore } from "./panelStore"; import { syncVuextoGalaxy } from "./syncVuextoGalaxy"; import { tagStore } from "./tagStore"; @@ -48,7 +47,6 @@ export function createStore() { datasetPathDestination: datasetPathDestinationStore, invocations: invocationStore, gridSearch: gridSearchStore, - panels: panelStore, tags: tagStore, }, }; diff --git a/client/src/store/panelStore.js b/client/src/store/panelStore.js deleted file mode 100644 index 93dea90d221f..000000000000 --- a/client/src/store/panelStore.js +++ /dev/null @@ -1,66 +0,0 @@ -import axios from "axios"; -import { getAppRoot } from "onload/loadConfig"; -import Vue from "vue"; - -export const state = { - panel: {}, - currentPanelView: null, -}; - -const getters = { - panel: (state) => (panelView) => { - return state.panel[panelView] || []; - }, - currentPanel: (state) => { - const effectiveView = state.currentPanelView || "default"; - const val = state.panel[effectiveView] || []; - return val; - }, - currentPanelView: (state) => { - return state.currentPanelView; - }, -}; - -const actions = { - initCurrentPanelView: async ({ commit, state, dispatch }, siteDefaultPanelView) => { - const panelView = state.currentPanelView || siteDefaultPanelView; - if (state.currentPanelView == null) { - commit("setCurrentPanelView", { panelView }); - } - const response = await axios.get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`).catch((error) => { - if (error.response && error.response.status == 400) { - // Assume the stored panelView disappeared, revert to the panel default for this site. - dispatch("setCurrentPanelView", siteDefaultPanelView); - } - }); - if (response !== undefined) { - commit("savePanelView", { panelView, panel: response.data }); - } - }, - setCurrentPanelView: async ({ commit }, panelView) => { - commit("setCurrentPanelView", { panelView }); - const { data } = await axios.get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`); - commit("savePanelView", { panelView, panel: data }); - }, - fetchPanel: async ({ commit }, panelView) => { - const { data } = await axios.get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`); - commit("savePanelView", { panelView, panel: data }); - }, -}; - -const mutations = { - savePanelView: (state, { panelView, panel }) => { - Vue.set(state.panel, panelView, panel); - }, - setCurrentPanelView: (state, { panelView }) => { - state.currentPanelView = panelView; - }, -}; - -export const panelStore = { - namespaced: true, - state, - getters, - actions, - mutations, -}; diff --git a/client/src/stores/toolStore.ts b/client/src/stores/toolStore.ts index b3e7f639f796..f6c969cc46c7 100644 --- a/client/src/stores/toolStore.ts +++ b/client/src/stores/toolStore.ts @@ -156,7 +156,7 @@ export const useToolStore = defineStore("toolStore", () => { if (!loading.value && !allToolsByIdFetched.value) { loading.value = true; await axios - .get(`${getAppRoot()}api/tools?in_panel=False`) + .get(`${getAppRoot()}api/tool_panel?in_panel=False`) .then(({ data }) => { saveAllTools(data.tools); loading.value = false; @@ -177,7 +177,7 @@ export const useToolStore = defineStore("toolStore", () => { currentPanelView.value = panelView; } await axios - .get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`) + .get(`${getAppRoot()}api/tool_panel?in_panel=true&view=${panelView}`) .then(({ data }) => { loading.value = false; savePanelView(panelView, data[panelView]); @@ -199,14 +199,14 @@ export const useToolStore = defineStore("toolStore", () => { return; } loading.value = true; - const { data } = await axios.get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`); + const { data } = await axios.get(`${getAppRoot()}api/tool_panel?in_panel=true&view=${panelView}`); savePanelView(panelView, data[panelView]); loading.value = false; } } async function fetchPanel(panelView: string) { - const { data } = await axios.get(`${getAppRoot()}api/tools?in_panel=true&view=${panelView}`); + const { data } = await axios.get(`${getAppRoot()}api/tool_panel?in_panel=true&view=${panelView}`); savePanelView(panelView, data[panelView]); } diff --git a/lib/galaxy/webapps/galaxy/api/tools.py b/lib/galaxy/webapps/galaxy/api/tools.py index 2ae7d7b9615b..cbce36d549c2 100644 --- a/lib/galaxy/webapps/galaxy/api/tools.py +++ b/lib/galaxy/webapps/galaxy/api/tools.py @@ -167,6 +167,62 @@ def index(self, trans: GalaxyWebTransaction, **kwds): detected_versions = self.service._detect(trans, tool_id) return detected_versions + # Return everything. + try: + return self.app.toolbox.to_dict( + trans, in_panel=in_panel, trackster=trackster, tool_help=tool_help, view=view + ) + except exceptions.MessageException: + raise + except Exception: + raise exceptions.InternalServerError("Error: Could not convert toolbox to dictionary") + + @expose_api_anonymous_and_sessionless + def panel_view(self, trans: GalaxyWebTransaction, **kwds): + """ + GET /api/tool_panel + + returns a dictionary of all tools or panels defined by parameters + + :param in_panel: if true, tool sections are returned in panel + structure, with tool ids and labels + :param view: ToolBox view to apply (default is 'default') + :param trackster: if true, only tools that are compatible with + Trackster are returned + :param q: if present search on the given query will be performed + :param tool_id: if present the given tool_id will be searched for + all installed versions + """ + + # Read params. + in_panel = util.string_as_bool(kwds.get("in_panel", "True")) + trackster = util.string_as_bool(kwds.get("trackster", "False")) + q = kwds.get("q", "") + tool_id = kwds.get("tool_id", "") + tool_help = util.string_as_bool(kwds.get("tool_help", "False")) + view = kwds.get("view", None) + + # Find whether to search. + if q: + hits = self.service._search(q, view) + results = [] + if hits: + for hit in hits: + try: + tool = self.service._get_tool(trans, hit, user=trans.user) + if tool: + results.append(tool.id) + except exceptions.AuthenticationFailed: + pass + except exceptions.ObjectNotFound: + pass + return results + + # Find whether to detect. + if tool_id: + detected_versions = self.service._detect(trans, tool_id) + return detected_versions + # Return everything. try: return self.app.toolbox.to_panel_view( diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 8d158a8cbf8c..33f1a85e9928 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -367,6 +367,7 @@ def populate_api_routes(webapp, app): # ====== TOOLS API ====== # ======================= + webapp.mapper.connect("/api/tool_panel", action="panel_view", controller="tools") webapp.mapper.connect("/api/tools/all_requirements", action="all_requirements", controller="tools") webapp.mapper.connect("/api/tools/error_stack", action="error_stack", controller="tools") webapp.mapper.connect("/api/tools/{id:.+?}/build", action="build", controller="tools") diff --git a/lib/galaxy_test/api/test_tools.py b/lib/galaxy_test/api/test_tools.py index 11649b1a9668..a217c572920c 100644 --- a/lib/galaxy_test/api/test_tools.py +++ b/lib/galaxy_test/api/test_tools.py @@ -164,7 +164,7 @@ def test_search_grep(self): assert "Grep1" in get_response def test_no_panel_index(self): - index = self._get("tools", data=dict(in_panel=False)) + index = self._get("tool_panel", data=dict(in_panel=False)) tools_index = index.json().get("tools", {}) # No need to flatten out sections, with in_panel=False, only tools by # ids are returned. @@ -2640,7 +2640,7 @@ def _run_cat1(self, history_id, inputs, assert_ok=False, **kwargs): return self._run("cat1", history_id, inputs, assert_ok=assert_ok, **kwargs) def __tool_ids(self): - index = self._get("tools") + index = self._get("tool_panel") tools_index = index.json().get("default", {}) # In panels by default, so flatten out sections... tool_ids = [] diff --git a/lib/galaxy_test/base/populators.py b/lib/galaxy_test/base/populators.py index 32bfde4ddae6..1c948bc88508 100644 --- a/lib/galaxy_test/base/populators.py +++ b/lib/galaxy_test/base/populators.py @@ -154,7 +154,7 @@ def skip_without_tool(tool_id: str): def method_wrapper(method): def get_tool_ids(api_test_case: HasAnonymousGalaxyInteractor): interactor = api_test_case.anonymous_galaxy_interactor - index = interactor.get("tools", data=dict(in_panel=False)) + index = interactor.get("tool_panel", data=dict(in_panel=False)) api_asserts.assert_status_code_is_ok(index, "Failed to fetch toolbox for target Galaxy.") tools = index.json().get("tools", {}) # `in_panel=False`, so we have a toolsById dict... @@ -2287,7 +2287,7 @@ def _run_cwl_tool_job( if os.path.exists(tool_id): raw_tool_id = os.path.basename(tool_id) - index = self.dataset_populator._get("tools", data=dict(in_panel=False)) + index = self.dataset_populator._get("tool_panel", data=dict(in_panel=False)) index.raise_for_status() tools = index.json().get("tools", {}) tool_ids = list(tools.keys()) diff --git a/lib/tool_shed/test/functional/test_galaxy_install.py b/lib/tool_shed/test/functional/test_galaxy_install.py index 5b295e3333fa..21cc8b1dfc93 100644 --- a/lib/tool_shed/test/functional/test_galaxy_install.py +++ b/lib/tool_shed/test/functional/test_galaxy_install.py @@ -10,7 +10,7 @@ def test_install_simple_tool(self): installable_revisions = populator.get_ordered_installable_revisions(owner, name) latest_install_revision = installable_revisions.__root__[-1] self.install_repository(owner, name, latest_install_revision, tool_shed_url=self.url) - response = self.galaxy_interactor._get("tools?in_panel=False") + response = self.galaxy_interactor._get("tool_panel?in_panel=False") response.raise_for_status() expected_tool = populator.tool_guid(self, repository, "Add_a_column1", "1.1.0") tool_ids = list((response.json().get("tools", {})).keys()) @@ -26,7 +26,7 @@ def test_install_simple_after_repository_metadata_reset(self): metadata_response = populator.reset_metadata(repository) assert metadata_response.status == "ok" self.install_repository(owner, name, latest_install_revision, tool_shed_url=self.url) - response = self.galaxy_interactor._get("tools?in_panel=False") + response = self.galaxy_interactor._get("tool_panel?in_panel=False") response.raise_for_status() expected_tool = f"{self.host}:{self.port}/repos/{owner}/{name}/Add_a_column1/1.1.0" tool_ids = list((response.json().get("tools", {})).keys()) diff --git a/test/integration/test_edam_toolbox.py b/test/integration/test_edam_toolbox.py index 5afe9acbdca2..05b923c544ae 100644 --- a/test/integration/test_edam_toolbox.py +++ b/test/integration/test_edam_toolbox.py @@ -10,7 +10,7 @@ def handle_galaxy_config_kwds(cls, config): config["edam_panel_views"] = "merged" def test_edam_toolbox(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="ontology:edam_merged")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="ontology:edam_merged")) index.raise_for_status() index_panel = index.json().get("ontology:edam_merged", {}) sections = [x for _, x in index_panel.items() if x["model_class"] == "ToolSection"] @@ -40,7 +40,7 @@ def handle_galaxy_config_kwds(cls, config): config["default_panel_view"] = "ontology:edam_topics" def test_edam_toolbox(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True)) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True)) index.raise_for_status() index_panel = index.json().get("ontology:edam_topics", {}) sections = [x for _, x in index_panel.items() if x["model_class"] == "ToolSection"] diff --git a/test/integration/test_panel_views.py b/test/integration/test_panel_views.py index 91a3ce82e14c..e1c60e0cb938 100644 --- a/test/integration/test_panel_views.py +++ b/test/integration/test_panel_views.py @@ -16,7 +16,7 @@ def handle_galaxy_config_kwds(cls, config): config["panel_views_dir"] = PANEL_VIEWS_DIR_1 def test_section_copy(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="filter")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="filter")) index_panel = index.json().get("filter", {}) sections = get_sections(index_panel) section_names = [s["name"] for s in sections] @@ -24,11 +24,11 @@ def test_section_copy(self): assert "For Tours" in section_names def test_custom_label_order(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="my-custom")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="my-custom")) verify_my_custom(index) def test_filtering_sections_by_tool_id(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_2")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_2")) index.raise_for_status() index_panel = index.json().get("custom_2", {}) sections = get_sections(index_panel) @@ -38,11 +38,11 @@ def test_filtering_sections_by_tool_id(self): assert len(tools) == 3, len(tools) def test_filtering_sections_by_tool_id_regex(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_3")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_3")) verify_custom_regex_filtered(index, "custom_3") def test_filtering_root_by_type(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_4")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_4")) index.raise_for_status() index_panel = index.json().get("custom_4", {}) assert len(index_panel) == 2 @@ -51,7 +51,7 @@ def test_filtering_root_by_type(self): assert list(index_panel.keys()) == ["empty_list", "count_list"] def test_custom_section_def(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_6")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_6")) index.raise_for_status() index_panel = index.json().get("custom_6", {}) assert len(index_panel) == 1 @@ -67,11 +67,11 @@ def test_custom_section_def(self): ] def test_section_embed(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_5")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_5")) verify_custom_embed(index, "custom_5") def test_section_embed_filtering(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_7")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_7")) index.raise_for_status() index_panel = index.json().get("custom_7", {}) assert len(index_panel) == 1 @@ -84,26 +84,26 @@ def test_section_embed_filtering(self): assert section_elems[3]["model_class"] == "ToolSectionLabel" def test_section_reference_by_name(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_8")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_8")) verify_custom_embed(index, "custom_8") def test_section_alias(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_9")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_9")) verify_custom_regex_filtered(index, "custom_9") def test_expand_section_aliases(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_10")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_10")) index.raise_for_status() index_panel = index.json().get("custom_10", {}) assert len(index_panel) == 2 assert model_classes(index_panel) == ["ToolSection", "ToolSection"] def test_global_filters(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_11")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_11")) verify_custom_regex_filtered(index, "custom_11") def test_global_filters_on_integrated_panel(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True, view="custom_12")) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True, view="custom_12")) index.raise_for_status() index_panel = index.json().get("custom_12", {}) sections = get_sections(index_panel) @@ -153,7 +153,7 @@ def handle_galaxy_config_kwds(cls, config): config["default_panel_view"] = "my-custom" def test_custom_label_order(self): - index = self.galaxy_interactor.get("tools", data=dict(in_panel=True)) + index = self.galaxy_interactor.get("tool_panel", data=dict(in_panel=True)) verify_my_custom(index)