diff --git a/client/packages/api-client/src/schema/schema.ts b/client/packages/api-client/src/schema/schema.ts index ade9a2f17a9..4717c19ea79 100644 --- a/client/packages/api-client/src/schema/schema.ts +++ b/client/packages/api-client/src/schema/schema.ts @@ -5644,6 +5644,23 @@ export interface paths { patch?: never; trace?: never; }; + "/api/tools/{tool_id}/interop": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Return Galaxy's meta model description of the tool's metadata, inputs, and outputs. */ + get: operations["tools__interop"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/tools/{tool_id}/parameter_landing_request_schema": { parameters: { query?: never; @@ -5698,6 +5715,74 @@ export interface paths { patch?: never; trace?: never; }; + "/api/tools/{tool_id}/versions/{tool_version}/interop": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Return Galaxy's meta model description of the tool's metadata, inputs, and outputs. */ + get: operations["tools__versioned_interop"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tools/{tool_id}/versions/{tool_version}/parameter_landing_request_schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Return a JSON schema description of the tool's inputs for the tool landing request API. */ + get: operations["tools__versioned_parameter_landing_request_schema"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tools/{tool_id}/versions/{tool_version}/parameter_request_schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Return a JSON schema description of the tool's inputs for the tool request API. */ + get: operations["tools__versioned_parameter_request_schema"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tools/{tool_id}/versions/{tool_version}/parameter_test_case_xml_schema": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Return a JSON schema description of the tool's inputs for test case construction. */ + get: operations["tools__versioned_parameter_test_case_xml_schema"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/tours": { parameters: { query?: never; @@ -20036,6 +20121,18 @@ export interface components { */ output_name: string | null; }; + /** PackageRequirement */ + PackageRequirement: { + /** Name */ + name: string; + /** + * Type + * @constant + */ + type: "package"; + /** Version */ + version?: string | null; + }; /** * PageContentFormat * @enum {string} @@ -20479,6 +20576,79 @@ export interface components { */ workbook_type: "datasets" | "collection" | "collections"; }; + /** ParsedTool */ + ParsedTool: { + /** Citations */ + citations: components["schemas"]["Citation"][]; + /** Containers */ + containers?: components["schemas"]["Container"][]; + /** Description */ + description: string | null; + /** Edam Operations */ + edam_operations: string[]; + /** Edam Topics */ + edam_topics: string[]; + help: components["schemas"]["HelpContent"] | null; + /** Id */ + id: string; + /** Inputs */ + inputs: ( + | components["schemas"]["CwlIntegerParameterModel"] + | components["schemas"]["CwlFloatParameterModel"] + | components["schemas"]["CwlStringParameterModel"] + | components["schemas"]["CwlBooleanParameterModel"] + | components["schemas"]["CwlNullParameterModel"] + | components["schemas"]["CwlFileParameterModel"] + | components["schemas"]["CwlDirectoryParameterModel"] + | components["schemas"]["CwlUnionParameterModel"] + | components["schemas"]["TextParameterModel"] + | components["schemas"]["IntegerParameterModel"] + | components["schemas"]["FloatParameterModel"] + | components["schemas"]["BooleanParameterModel"] + | components["schemas"]["HiddenParameterModel"] + | components["schemas"]["SelectParameterModel"] + | components["schemas"]["DataParameterModel"] + | components["schemas"]["DataCollectionParameterModel"] + | components["schemas"]["DataColumnParameterModel"] + | components["schemas"]["DirectoryUriParameterModel"] + | components["schemas"]["RulesParameterModel"] + | components["schemas"]["DrillDownParameterModel"] + | components["schemas"]["GroupTagParameterModel"] + | components["schemas"]["BaseUrlParameterModel"] + | components["schemas"]["GenomeBuildParameterModel"] + | components["schemas"]["ColorParameterModel"] + | components["schemas"]["ConditionalParameterModel"] + | components["schemas"]["RepeatParameterModel"] + | components["schemas"]["SectionParameterModel"] + )[]; + /** License */ + license: string | null; + /** Name */ + name: string; + /** Outputs */ + outputs: ( + | components["schemas"]["ToolOutputDataset"] + | components["schemas"]["ToolOutputCollection"] + | components["schemas"]["ToolOutputText"] + | components["schemas"]["ToolOutputInteger"] + | components["schemas"]["ToolOutputFloat"] + | components["schemas"]["ToolOutputBoolean"] + )[]; + /** Profile */ + profile: string | null; + /** Requirements */ + requirements?: ( + | components["schemas"]["PackageRequirement"] + | components["schemas"]["SetEnvironmentRequirement"] + | components["schemas"]["ResourceRequirement"] + | components["schemas"]["JavascriptRequirement"] + )[]; + stdio?: components["schemas"]["Stdio"]; + /** Version */ + version: string | null; + /** Xrefs */ + xrefs: components["schemas"]["XrefDict-Output"][]; + }; /** ParsedWorkbook */ ParsedWorkbook: { /** Extra Columns */ @@ -22476,6 +22646,16 @@ export interface components { */ version: string; }; + /** SetEnvironmentRequirement */ + SetEnvironmentRequirement: { + /** Environment */ + environment: string; + /** + * Type + * @constant + */ + type: "set_environment"; + }; /** SetSlugPayload */ SetSlugPayload: { /** @@ -22934,6 +23114,37 @@ export interface components { | "CANCELED" | "CANCELING" | "PREEMPTED"; + /** Stdio */ + Stdio: { + /** Exit Codes */ + exit_codes?: components["schemas"]["StdioExitCode"][]; + /** Regexes */ + regexes?: components["schemas"]["StdioRegex"][]; + }; + /** StdioExitCode */ + StdioExitCode: { + /** Desc */ + desc?: string | null; + /** Error Level */ + error_level: number; + /** Range End */ + range_end: number | ("-inf" | "inf"); + /** Range Start */ + range_start: number | ("-inf" | "inf"); + }; + /** StdioRegex */ + StdioRegex: { + /** Desc */ + desc?: string | null; + /** Error Level */ + error_level: number; + /** Match */ + match: string; + /** Stderr Match */ + stderr_match: boolean; + /** Stdout Match */ + stdout_match: boolean; + }; /** StepReferenceByLabel */ StepReferenceByLabel: { /** @@ -24472,6 +24683,191 @@ export interface components { */ uuid: string; }; + /** ToolOutputBoolean */ + ToolOutputBoolean: { + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "boolean"; + }; + /** ToolOutputCollection */ + ToolOutputCollection: { + /** Collection Type */ + collection_type?: string | null; + /** Collection Type From Rules */ + collection_type_from_rules?: string | null; + /** Collection Type Source */ + collection_type_source?: string | null; + /** Discover Datasets */ + discover_datasets?: + | ( + | components["schemas"]["FilePatternDatasetCollectionDescription"] + | components["schemas"]["ToolProvidedMetadataDatasetCollection"] + )[] + | null; + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** Structured Like */ + structured_like?: string | null; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "collection"; + }; + /** ToolOutputDataset */ + ToolOutputDataset: { + /** Discover Datasets */ + discover_datasets?: + | ( + | components["schemas"]["FilePatternDatasetCollectionDescription"] + | components["schemas"]["ToolProvidedMetadataDatasetCollection"] + )[] + | null; + /** + * Format + * @description The short name for the output datatype. + */ + format: string; + /** + * Format Source + * @description This sets the data type of the output dataset(s) to be the same format as that of the specified tool input. + */ + format_source?: string | null; + /** + * from_work_dir + * @description Relative path to a file produced by the tool in its working directory. Output’s contents are set to this file’s contents. + */ + from_work_dir?: string | null; + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Metadata Source + * @description This copies the metadata information from the tool’s input dataset to serve as default for information that cannot be detected from the output. One prominent use case is interval data with a non-standard column order that cannot be deduced from a header line, but which is known to be identical in the input and output datasets. + */ + metadata_source?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** + * Precreate Directory + * @default false + */ + precreate_directory: boolean | null; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "data"; + }; + /** ToolOutputFloat */ + ToolOutputFloat: { + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "float"; + }; + /** ToolOutputInteger */ + ToolOutputInteger: { + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "integer"; + }; + /** ToolOutputText */ + ToolOutputText: { + /** + * Hidden + * @description If true, the output will not be shown in the history. + */ + hidden: boolean; + /** + * Label + * @description Output label. Will be used as dataset name in history. + */ + label?: string | null; + /** + * Name + * @description Parameter name. Used when referencing parameter in workflows. + */ + name: string; + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "text"; + }; /** ToolProvidedMetadataDatasetCollection */ ToolProvidedMetadataDatasetCollection: { /** @@ -26016,7 +26412,7 @@ export interface components { */ version: string; /** xrefs */ - xrefs?: components["schemas"]["XrefDict"][] | null; + xrefs?: components["schemas"]["XrefDict-Input"][] | null; }; /** * UserToolSource @@ -26123,7 +26519,7 @@ export interface components { */ version: string; /** xrefs */ - xrefs?: components["schemas"]["XrefDict"][] | null; + xrefs?: components["schemas"]["XrefDict-Output"][] | null; }; /** UserUpdatePayload */ UserUpdatePayload: { @@ -27299,12 +27695,19 @@ export interface components { target_uri: string; }; /** XrefDict */ - XrefDict: { + "XrefDict-Input": { /** type */ type: string; /** value */ value: string; }; + /** XrefDict */ + "XrefDict-Output": { + /** Type */ + type: string; + /** Value */ + value: string; + }; /** XrefItem */ XrefItem: { /** @@ -27927,7 +28330,7 @@ export interface components { */ version?: string | null; /** xrefs */ - xrefs?: components["schemas"]["XrefDict"][] | null; + xrefs?: components["schemas"]["XrefDict-Input"][] | null; }; /** * YamlToolTest @@ -48920,6 +49323,52 @@ export interface operations { }; }; }; + tools__interop: { + parameters: { + query?: { + tool_version?: string | null; + }; + header?: { + /** @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. */ + "run-as"?: string | null; + }; + path: { + /** @description The tool ID for the lineage stored in Galaxy's toolbox. */ + tool_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ParsedTool"]; + }; + }; + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + }; + }; tools__parameter_landing_request_schema: { parameters: { query?: { @@ -49058,6 +49507,190 @@ export interface operations { }; }; }; + tools__versioned_interop: { + parameters: { + query?: never; + header?: { + /** @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. */ + "run-as"?: string | null; + }; + path: { + /** @description The tool ID for the lineage stored in Galaxy's toolbox. */ + tool_id: string; + /** @description The full version string defined on the Galaxy tool wrapper. */ + tool_version: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ParsedTool"]; + }; + }; + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + }; + }; + tools__versioned_parameter_landing_request_schema: { + parameters: { + query?: never; + header?: { + /** @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. */ + "run-as"?: string | null; + }; + path: { + /** @description The tool ID for the lineage stored in Galaxy's toolbox. */ + tool_id: string; + /** @description The full version string defined on the Galaxy tool wrapper. */ + tool_version: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + }; + }; + tools__versioned_parameter_request_schema: { + parameters: { + query?: never; + header?: { + /** @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. */ + "run-as"?: string | null; + }; + path: { + /** @description The tool ID for the lineage stored in Galaxy's toolbox. */ + tool_id: string; + /** @description The full version string defined on the Galaxy tool wrapper. */ + tool_version: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + }; + }; + tools__versioned_parameter_test_case_xml_schema: { + parameters: { + query?: never; + header?: { + /** @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. */ + "run-as"?: string | null; + }; + path: { + /** @description The tool ID for the lineage stored in Galaxy's toolbox. */ + tool_id: string; + /** @description The full version string defined on the Galaxy tool wrapper. */ + tool_version: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageExceptionModel"]; + }; + }; + }; + }; index_api_tours_get: { parameters: { query?: never; diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 19610050d57..2fd93c3cec0 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -82,6 +82,7 @@ from galaxy.tool_util.loader import ( template_macro_params, ) from galaxy.tool_util.loader_directory import looks_like_a_tool +from galaxy.tool_util.model_factory import parse_tool from galaxy.tool_util.ontologies.ontology_data import ( biotools_reference, expand_ontology_data, @@ -132,6 +133,7 @@ from galaxy.tool_util.version import ( parse_version, ) from galaxy.tool_util.version_updates import WORKFLOW_SAFE_TOOL_VERSION_UPDATES +from galaxy.tool_util_models import ParsedTool from galaxy.tool_util_models.parameters import ( MaybeToolParameterBundle, ToolParameterBundleModel, @@ -203,6 +205,7 @@ from galaxy.util import ( in_directory, Params, parse_xml_string, + parse_xml_string_to_etree, rst_to_html, string_as_bool, unicodify, @@ -1101,6 +1104,17 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle): def history_manager(self): return self.app.history_manager + def parsed_tool(self) -> ParsedTool: + """Return a ParsedTool model for this tool. + + After tool loading, mem_optimize destroys the XML tree to save memory. + When that has happened, re-parse from the stored string representation. + """ + tool_source = self.tool_source + if getattr(tool_source, "root", None) is None: + tool_source = get_tool_source(xml_tree=parse_xml_string_to_etree(tool_source.to_string())) + return parse_tool(tool_source) + @property def _view(self): return self.app.dependency_resolvers_view diff --git a/lib/galaxy/webapps/galaxy/api/tools.py b/lib/galaxy/webapps/galaxy/api/tools.py index 4cad53e238d..2a5d016e03d 100644 --- a/lib/galaxy/webapps/galaxy/api/tools.py +++ b/lib/galaxy/webapps/galaxy/api/tools.py @@ -64,6 +64,7 @@ from galaxy.tool_util.parameters import ( from galaxy.tool_util.verify import ToolTestDescriptionDict from galaxy.tool_util_models import ( lift_user_tool_source, + ParsedTool, UserToolSource, ) from galaxy.tools.evaluation import global_tool_errors @@ -92,7 +93,10 @@ from galaxy.webapps.base.controller import UsesVisualizationMixin from galaxy.webapps.base.webapp import GalaxyWebTransaction from galaxy.webapps.galaxy.api.common import serve_workbook from galaxy.webapps.galaxy.services.base import tool_request_detailed_to_model -from galaxy.webapps.galaxy.services.tools import ToolsService +from galaxy.webapps.galaxy.services.tools import ( + get_tool, + ToolsService, +) from . import ( APIContentTypeRoute, as_form, @@ -152,6 +156,11 @@ ToolIDPathParam: str = Path( title="Tool ID", description="The tool ID for the lineage stored in Galaxy's toolbox.", ) +ToolVersionPathParam: str = Path( + ..., + title="Tool Version", + description="The full version string defined on the Galaxy tool wrapper.", +) ToolVersionQueryParam: str | None = Query(default=None, title="Tool Version", description="") @@ -433,6 +442,87 @@ class FetchTools: def tool_tags(self, trans: ProvidesHistoryContext = DependsOnTrans) -> dict[str, list[str]]: return self.service.curated_tool_tags_by_id(trans) + @router.get( + "/api/tools/{tool_id}/interop", + operation_id="tools__interop", + summary="Return Galaxy's meta model description of the tool's metadata, inputs, and outputs.", + ) + def parsed_tool( + self, + tool_id: str = ToolIDPathParam, + tool_version: str | None = ToolVersionQueryParam, + trans: ProvidesHistoryContext = DependsOnTrans, + ) -> ParsedTool: + return self._parsed_tool(trans, tool_id, tool_version) + + @router.get( + "/api/tools/{tool_id}/versions/{tool_version}/interop", + operation_id="tools__versioned_interop", + summary="Return Galaxy's meta model description of the tool's metadata, inputs, and outputs.", + ) + def parsed_tool_versioned( + self, + tool_id: str = ToolIDPathParam, + tool_version: str = ToolVersionPathParam, + trans: ProvidesHistoryContext = DependsOnTrans, + ) -> ParsedTool: + return self._parsed_tool(trans, tool_id, tool_version) + + @router.get( + "/api/tools/{tool_id}/versions/{tool_version}/parameter_request_schema", + operation_id="tools__versioned_parameter_request_schema", + summary="Return a JSON schema description of the tool's inputs for the tool request API.", + ) + def tool_state_request_versioned( + self, + tool_id: str = ToolIDPathParam, + tool_version: str = ToolVersionPathParam, + trans: ProvidesHistoryContext = DependsOnTrans, + ) -> Response: + tool_run_ref = ToolRunReference(tool_id=tool_id, tool_version=tool_version, tool_uuid=None) + inputs = self.service.inputs(trans, tool_run_ref) + return json_schema_response_for_tool_state_model(RequestToolState, inputs) + + @router.get( + "/api/tools/{tool_id}/versions/{tool_version}/parameter_landing_request_schema", + operation_id="tools__versioned_parameter_landing_request_schema", + summary="Return a JSON schema description of the tool's inputs for the tool landing request API.", + ) + def tool_state_landing_request_versioned( + self, + tool_id: str = ToolIDPathParam, + tool_version: str = ToolVersionPathParam, + trans: ProvidesHistoryContext = DependsOnTrans, + ) -> Response: + tool_run_ref = ToolRunReference(tool_id=tool_id, tool_version=tool_version, tool_uuid=None) + inputs = self.service.inputs(trans, tool_run_ref) + return json_schema_response_for_tool_state_model(LandingRequestToolState, inputs) + + @router.get( + "/api/tools/{tool_id}/versions/{tool_version}/parameter_test_case_xml_schema", + operation_id="tools__versioned_parameter_test_case_xml_schema", + summary="Return a JSON schema description of the tool's inputs for test case construction.", + ) + def tool_state_test_case_xml_versioned( + self, + tool_id: str = ToolIDPathParam, + tool_version: str = ToolVersionPathParam, + trans: ProvidesHistoryContext = DependsOnTrans, + ) -> Response: + tool_run_ref = ToolRunReference(tool_id=tool_id, tool_version=tool_version, tool_uuid=None) + inputs = self.service.inputs(trans, tool_run_ref) + return json_schema_response_for_tool_state_model(TestCaseToolState, inputs) + + def _parsed_tool( + self, + trans: ProvidesHistoryContext, + tool_id: str, + tool_version: str | None, + ) -> ParsedTool: + tool_run_ref = ToolRunReference(tool_id=tool_id, tool_version=tool_version, tool_uuid=None) + tool = get_tool(trans, tool_run_ref) + return tool.parsed_tool() + class ToolsController(BaseGalaxyAPIController, UsesVisualizationMixin): """ diff --git a/lib/galaxy_test/api/test_tools.py b/lib/galaxy_test/api/test_tools.py index f6f5eca28de..df43ada1dd5 100644 --- a/lib/galaxy_test/api/test_tools.py +++ b/lib/galaxy_test/api/test_tools.py @@ -768,6 +768,47 @@ class TestToolsApi(ApiTestCase, TestsTools): assert "--ex1" in option_values assert "ex2" in option_values + @skip_without_tool("gx_int") + def test_tool_interop(self): + """GET /api/tools/{tool_id}/interop returns ParsedTool JSON.""" + response = self._get("tools/gx_int/interop") + self._assert_status_code_is(response, 200) + interop = response.json() + assert interop["id"] == "gx_int" + assert "inputs" in interop + assert "outputs" in interop + assert len(interop["inputs"]) > 0 + + @skip_without_tool("gx_int") + def test_tool_interop_versioned(self): + """GET /api/tools/{tool_id}/versions/{version}/interop returns same result.""" + # Get version from the unversioned endpoint first + response = self._get("tools/gx_int/interop") + self._assert_status_code_is(response, 200) + interop = response.json() + version = interop["version"] + + versioned_response = self._get(f"tools/gx_int/versions/{version}/interop") + self._assert_status_code_is(versioned_response, 200) + versioned_interop = versioned_response.json() + assert versioned_interop["id"] == interop["id"] + assert versioned_interop["version"] == version + assert len(versioned_interop["inputs"]) == len(interop["inputs"]) + + @skip_without_tool("gx_int") + def test_versioned_schema_endpoints(self): + """Versioned /versions/{v}/parameter_*_schema endpoints mirror unversioned ones.""" + response = self._get("tools/gx_int/interop") + version = response.json()["version"] + + for schema_type in ["request", "landing_request", "test_case_xml"]: + unversioned = self._get(f"tools/gx_int/parameter_{schema_type}_schema") + self._assert_status_code_is(unversioned, 200) + + versioned = self._get(f"tools/gx_int/versions/{version}/parameter_{schema_type}_schema") + self._assert_status_code_is(versioned, 200) + assert unversioned.json() == versioned.json() + @skip_without_tool("test_data_source") def test_data_source_ok_request(self, mock_http_server): with self.dataset_populator.test_history() as history_id: diff --git a/lib/tool_shed/test/functional/test_shed_tools.py b/lib/tool_shed/test/functional/test_shed_tools.py index ffdb13ecf17..eb2d4ebcbe4 100644 --- a/lib/tool_shed/test/functional/test_shed_tools.py +++ b/lib/tool_shed/test/functional/test_shed_tools.py @@ -70,6 +70,20 @@ class TestShedToolsApi(ShedApiTestCase): tool_response = self.api_interactor.get(url) tool_response.raise_for_status() + def test_tool_interop(self): + populator = self.populator + repository = populator.setup_column_maker_repo(prefix="toolinterop") + tool_id = populator.tool_guid(self, repository, "Add_a_column1") + tool_shed_base, encoded_tool_id = encode_identifier(tool_id) + url = f"tools/{encoded_tool_id}/versions/1.1.0/interop" + tool_response = self.api_interactor.get(url) + tool_response.raise_for_status() + parsed = tool_response.json() + assert parsed["id"] == "Add_a_column1" + assert parsed["version"] == "1.1.0" + assert "inputs" in parsed + assert "outputs" in parsed + def test_tool_source(self): populator = self.populator repository = populator.setup_column_maker_repo(prefix="toolsource") diff --git a/lib/tool_shed/webapp/api2/tools.py b/lib/tool_shed/webapp/api2/tools.py index bbc9ace773c..4dfa231697b 100644 --- a/lib/tool_shed/webapp/api2/tools.py +++ b/lib/tool_shed/webapp/api2/tools.py @@ -157,6 +157,19 @@ class FastAPITools: ) -> ShedParsedTool: return parsed_tool_model_cached_for(trans, tool_id, tool_version) + @router.get( + "/api/tools/{tool_id}/versions/{tool_version}/interop", + operation_id="tools__interop", + summary="Return Galaxy's meta model description of the tool's metadata, inputs, and outputs.", + ) + def interop( + self, + trans: SessionRequestContext = DependsOnTrans, + tool_id: str = TOOL_ID_PATH_PARAM, + tool_version: str = TOOL_VERSION_PATH_PARAM, + ) -> ShedParsedTool: + return parsed_tool_model_cached_for(trans, tool_id, tool_version) + @router.get( "/api/tools/{tool_id}/versions/{tool_version}/parameter_request_schema", operation_id="tools__parameter_request_schema", diff --git a/lib/tool_shed/webapp/frontend/src/schema/schema.ts b/lib/tool_shed/webapp/frontend/src/schema/schema.ts index d284f0cfd53..29aad10c331 100644 --- a/lib/tool_shed/webapp/frontend/src/schema/schema.ts +++ b/lib/tool_shed/webapp/frontend/src/schema/schema.ts @@ -587,6 +587,23 @@ export interface paths { patch?: never trace?: never } + "/api/tools/{tool_id}/versions/{tool_version}/interop": { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Return Galaxy's meta model description of the tool's metadata, inputs, and outputs. */ + get: operations["tools__interop"] + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } "/api/tools/{tool_id}/versions/{tool_version}/parameter_landing_request_schema": { parameters: { query?: never @@ -5676,6 +5693,49 @@ export interface operations { } } } + tools__interop: { + parameters: { + query?: never + header?: never + path: { + /** @description See also https://ga4gh.github.io/tool-registry-service-schemas/DataModel/#trs-tool-and-trs-tool-version-ids */ + tool_id: string + /** @description The full version string defined on the Galaxy tool wrapper. */ + tool_version: string + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown + } + content: { + "application/json": components["schemas"]["ShedParsedTool"] + } + } + /** @description Request Error */ + "4XX": { + headers: { + [name: string]: unknown + } + content: { + "application/json": components["schemas"]["MessageExceptionModel"] + } + } + /** @description Server Error */ + "5XX": { + headers: { + [name: string]: unknown + } + content: { + "application/json": components["schemas"]["MessageExceptionModel"] + } + } + } + } tools__parameter_landing_request_schema: { parameters: { query?: never