ecosyste-ms-cli 1.4.0__tar.gz → 1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ecosyste_ms_cli-1.4.2/CHANGELOG.md +21 -0
- {ecosyste_ms_cli-1.4.0/ecosyste_ms_cli.egg-info → ecosyste_ms_cli-1.4.2}/PKG-INFO +1 -1
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2/ecosyste_ms_cli.egg-info}/PKG-INFO +1 -1
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosyste_ms_cli.egg-info/SOURCES.txt +4 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/__init__.py +1 -1
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/cli.py +1 -103
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/advisories.py +21 -38
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/decorators.py +77 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/dependabot.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/diff.py +58 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/execution.py +49 -34
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/generator.py +24 -20
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/advisories.py +17 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/archives.py +13 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/base.py +92 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/commits.py +15 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/default.py +14 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/dependabot.py +29 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/diff.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/docker.py +22 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/issues.py +26 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/licenses.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/opencollective.py +18 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/handlers/packages.py +0 -61
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/parser.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/repos.py +38 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/resolve.py +17 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/sbom.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/sponsors.py +13 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/summary.py +18 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/handlers/timeline.py +16 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/licenses.py +55 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/packages.py +51 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/parser.py +55 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/purl_options.py +82 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/repos.py +20 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/resolve.py +66 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/commands/sbom.py +55 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/constants.py +3 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/helpers/build_kwargs.py +17 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/helpers/click_params.py +97 -0
- ecosyste_ms_cli-1.4.2/ecosystems_cli/helpers/job_polling.py +149 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/print_output.py +31 -12
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/purl_parser.py +39 -1
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/mcp_server.py +77 -61
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/openapi_client.py +15 -3
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/pyproject.toml +1 -1
- ecosyste_ms_cli-1.4.2/tests/test_handlers.py +57 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_mcp_server.py +100 -31
- ecosyste_ms_cli-1.4.2/tests/test_openapi_client.py +421 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_print_output.py +51 -0
- ecosyste_ms_cli-1.4.0/CHANGELOG.md +0 -7
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/decorators.py +0 -101
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/dependabot.py +0 -5
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/diff.py +0 -144
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/advisories.py +0 -73
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/archives.py +0 -40
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/base.py +0 -38
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/commits.py +0 -76
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/default.py +0 -40
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/dependabot.py +0 -205
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/diff.py +0 -72
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/docker.py +0 -142
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/issues.py +0 -87
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/licenses.py +0 -52
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/opencollective.py +0 -86
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/parser.py +0 -57
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/repos.py +0 -97
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/resolve.py +0 -68
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/sbom.py +0 -52
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/sponsors.py +0 -52
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/summary.py +0 -81
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/handlers/timeline.py +0 -45
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/licenses.py +0 -135
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/packages.py +0 -151
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/parser.py +0 -135
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/repos.py +0 -5
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/resolve.py +0 -160
- ecosyste_ms_cli-1.4.0/ecosystems_cli/commands/sbom.py +0 -135
- ecosyste_ms_cli-1.4.0/ecosystems_cli/helpers/click_params.py +0 -49
- ecosyste_ms_cli-1.4.0/tests/test_openapi_client.py +0 -187
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/LICENSE +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/MANIFEST.in +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/Readme.md +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosyste_ms_cli.egg-info/dependency_links.txt +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosyste_ms_cli.egg-info/entry_points.txt +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosyste_ms_cli.egg-info/requires.txt +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosyste_ms_cli.egg-info/top_level.txt +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/__main__.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/__init__.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/advisories.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/archives.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/commits.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/dependabot.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/diff.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/docker.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/issues.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/licenses.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/opencollective.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/packages.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/parser.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/registries.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/repos.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/resolve.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/sbom.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/sponsors.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/summary.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/apis/timeline.openapi.yaml +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/__init__.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/archives.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/commits.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/docker.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/handlers/__init__.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/handlers/factory.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/issues.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/mcp.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/opencollective.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/sponsors.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/summary.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/commands/timeline.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/exceptions.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/flatten_dict.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/format_value.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/get_domain.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/load_api_spec.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/print_error.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/ecosystems_cli/helpers/print_operations.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/setup.cfg +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_cli.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_cli_domain.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_constants.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_format_value.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_help_output.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_load_api_spec.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_package_metadata.py +0 -0
- {ecosyste_ms_cli-1.4.0 → ecosyste_ms_cli-1.4.2}/tests/test_print_operations.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog for v1.4.2
|
|
2
|
+
|
|
3
|
+
## Changes since v1.4.1
|
|
4
|
+
|
|
5
|
+
- refactor: call_tool tests (42a5ae5)
|
|
6
|
+
- refactor: sleep handling in polling (6566f5e)
|
|
7
|
+
- fix: date putput in json (9d87d5e)
|
|
8
|
+
- fix: Non-dict list responses crash table/TSV output. (cf09255)
|
|
9
|
+
- refactor: merge purl precedence (31bc8b8)
|
|
10
|
+
- refactor: merge purl precedence code (a19038b)
|
|
11
|
+
- refactor: archives handler simplified (51ec6cc)
|
|
12
|
+
- refactor: Unify the three `--purl` mechanisms. (86cc871)
|
|
13
|
+
- refactor: Delete `DefaultOperationHandler`'s stale config (1826703)
|
|
14
|
+
- refactor: add tests for openapi (5597b66)
|
|
15
|
+
- fix: passed body to request (159ecb7)
|
|
16
|
+
- refactor: several fixes\ \ * JSONL/TSV wrapping error fixed\ * poll loop with timeout\ * mcp events improved \ * default --max-wait of 600s \ (29f1b8d)
|
|
17
|
+
- refactor: the big clean\ \ * remove dead code\ * sync experimental mcp with all apis\ * handler consolidation completed\ * polling simplified and less duplicated code\ * packages.py --purl cleanup (a1b8915)
|
|
18
|
+
- refactor: replace_auto_command and Lift `_extract_param` to `handlers/base.py` (4171d09)
|
|
19
|
+
- refactor: polling (3a77384)
|
|
20
|
+
- refactor: star maintianing a build kwargs method (f12e930)
|
|
21
|
+
- refactor: Appending the apply_purl helper (d2a0eb0)
|
|
@@ -51,6 +51,7 @@ ecosystems_cli/commands/mcp.py
|
|
|
51
51
|
ecosystems_cli/commands/opencollective.py
|
|
52
52
|
ecosystems_cli/commands/packages.py
|
|
53
53
|
ecosystems_cli/commands/parser.py
|
|
54
|
+
ecosystems_cli/commands/purl_options.py
|
|
54
55
|
ecosystems_cli/commands/repos.py
|
|
55
56
|
ecosystems_cli/commands/resolve.py
|
|
56
57
|
ecosystems_cli/commands/sbom.py
|
|
@@ -78,10 +79,12 @@ ecosystems_cli/commands/handlers/sbom.py
|
|
|
78
79
|
ecosystems_cli/commands/handlers/sponsors.py
|
|
79
80
|
ecosystems_cli/commands/handlers/summary.py
|
|
80
81
|
ecosystems_cli/commands/handlers/timeline.py
|
|
82
|
+
ecosystems_cli/helpers/build_kwargs.py
|
|
81
83
|
ecosystems_cli/helpers/click_params.py
|
|
82
84
|
ecosystems_cli/helpers/flatten_dict.py
|
|
83
85
|
ecosystems_cli/helpers/format_value.py
|
|
84
86
|
ecosystems_cli/helpers/get_domain.py
|
|
87
|
+
ecosystems_cli/helpers/job_polling.py
|
|
85
88
|
ecosystems_cli/helpers/load_api_spec.py
|
|
86
89
|
ecosystems_cli/helpers/print_error.py
|
|
87
90
|
ecosystems_cli/helpers/print_operations.py
|
|
@@ -91,6 +94,7 @@ tests/test_cli.py
|
|
|
91
94
|
tests/test_cli_domain.py
|
|
92
95
|
tests/test_constants.py
|
|
93
96
|
tests/test_format_value.py
|
|
97
|
+
tests/test_handlers.py
|
|
94
98
|
tests/test_help_output.py
|
|
95
99
|
tests/test_load_api_spec.py
|
|
96
100
|
tests/test_mcp_server.py
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"""Command line interface for ecosystems CLI."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
|
-
from typing import Any, Dict, List, Optional
|
|
5
|
-
|
|
6
3
|
import click
|
|
7
|
-
from rich.console import Console
|
|
8
|
-
from rich.panel import Panel
|
|
9
4
|
|
|
10
5
|
from ecosystems_cli import __version__
|
|
11
6
|
from ecosystems_cli.commands.advisories import advisories
|
|
@@ -29,18 +24,8 @@ from ecosystems_cli.commands.timeline import timeline
|
|
|
29
24
|
from ecosystems_cli.constants import (
|
|
30
25
|
DEFAULT_OUTPUT_FORMAT,
|
|
31
26
|
DEFAULT_TIMEOUT,
|
|
32
|
-
ERROR_PANEL_STYLE,
|
|
33
|
-
ERROR_PREFIX,
|
|
34
27
|
OUTPUT_FORMATS,
|
|
35
28
|
)
|
|
36
|
-
from ecosystems_cli.exceptions import EcosystemsCLIError, JSONParseError
|
|
37
|
-
from ecosystems_cli.helpers.format_value import format_value
|
|
38
|
-
from ecosystems_cli.helpers.get_domain import build_base_url, get_domain_with_precedence
|
|
39
|
-
from ecosystems_cli.helpers.print_operations import print_operations
|
|
40
|
-
from ecosystems_cli.helpers.print_output import print_output
|
|
41
|
-
from ecosystems_cli.openapi_client import _factory as api_factory
|
|
42
|
-
|
|
43
|
-
console = Console()
|
|
44
29
|
|
|
45
30
|
|
|
46
31
|
@click.group(invoke_without_command=True)
|
|
@@ -97,20 +82,8 @@ def main(ctx, timeout, format, domain, mailto, install_completion):
|
|
|
97
82
|
ctx.obj["mailto"] = mailto
|
|
98
83
|
|
|
99
84
|
|
|
100
|
-
# Command Registration Strategy:
|
|
101
|
-
# We use a hybrid approach for command registration to balance usability and flexibility:
|
|
102
|
-
#
|
|
103
|
-
# 1. High-level API commands (e.g., 'ecosystems repos', 'ecosystems packages')
|
|
104
|
-
# - Registered via COMMAND_REGISTRY below
|
|
105
|
-
# - Provide user-friendly commands with custom logic and convenience methods
|
|
106
|
-
# - Each command is a BaseCommand subclass with tailored functionality
|
|
107
|
-
#
|
|
108
|
-
# 2. Low-level operation commands (e.g., 'ecosystems op repos get_topic')
|
|
109
|
-
# - Registered dynamically in register_op_commands()
|
|
110
|
-
# - Provide direct access to all API operations
|
|
111
|
-
# - Auto-generated from OpenAPI specifications
|
|
112
|
-
|
|
113
85
|
# Command registry - maps API names to their command instances
|
|
86
|
+
# (each value is a Click group generated from an OpenAPI spec).
|
|
114
87
|
COMMAND_REGISTRY = {
|
|
115
88
|
"advisories": advisories,
|
|
116
89
|
"archives": archives,
|
|
@@ -137,84 +110,9 @@ for api_name, command in COMMAND_REGISTRY.items():
|
|
|
137
110
|
|
|
138
111
|
# Register MCP server command
|
|
139
112
|
main.add_command(mcp)
|
|
140
|
-
# Dynamic op commands have been removed
|
|
141
113
|
|
|
142
114
|
|
|
143
115
|
@main.command(name="version", help="Show the ecosystems CLI version and exit.")
|
|
144
116
|
def version():
|
|
145
117
|
"""Print the installed ecosystems CLI version."""
|
|
146
118
|
click.echo(__version__)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def _parse_json_param(param: Optional[str]) -> Optional[Dict]:
|
|
150
|
-
"""Parse JSON parameter if provided."""
|
|
151
|
-
if not param:
|
|
152
|
-
return None
|
|
153
|
-
try:
|
|
154
|
-
return json.loads(param)
|
|
155
|
-
except json.JSONDecodeError as e:
|
|
156
|
-
raise JSONParseError(f"Invalid JSON: {param}. Error: {str(e)}")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _call_operation(api: str, operation: str, path_params: str, query_params: str, body: str, context=None):
|
|
160
|
-
"""Call an operation on the specified API."""
|
|
161
|
-
# Get timeout, format, and domain from context if available
|
|
162
|
-
if context and hasattr(context, "obj"):
|
|
163
|
-
timeout = context.obj.get("timeout", DEFAULT_TIMEOUT)
|
|
164
|
-
format_type = context.obj.get("format", DEFAULT_OUTPUT_FORMAT)
|
|
165
|
-
domain = context.obj.get("domain")
|
|
166
|
-
else:
|
|
167
|
-
timeout = DEFAULT_TIMEOUT
|
|
168
|
-
format_type = DEFAULT_OUTPUT_FORMAT
|
|
169
|
-
domain = None
|
|
170
|
-
|
|
171
|
-
# Get domain with proper precedence
|
|
172
|
-
final_domain = get_domain_with_precedence(api, domain)
|
|
173
|
-
base_url = build_base_url(final_domain, api)
|
|
174
|
-
|
|
175
|
-
try:
|
|
176
|
-
# Parse parameters
|
|
177
|
-
path_params_dict = _parse_json_param(path_params)
|
|
178
|
-
query_params_dict = _parse_json_param(query_params)
|
|
179
|
-
body_dict = _parse_json_param(body)
|
|
180
|
-
|
|
181
|
-
result = api_factory.call(
|
|
182
|
-
api_name=api,
|
|
183
|
-
operation_id=operation,
|
|
184
|
-
path_params=path_params_dict,
|
|
185
|
-
query_params=query_params_dict,
|
|
186
|
-
body=body_dict,
|
|
187
|
-
timeout=timeout,
|
|
188
|
-
base_url=base_url,
|
|
189
|
-
)
|
|
190
|
-
_print_output(result, format_type)
|
|
191
|
-
except JSONParseError as e:
|
|
192
|
-
_print_error(str(e))
|
|
193
|
-
except EcosystemsCLIError as e:
|
|
194
|
-
_print_error(str(e))
|
|
195
|
-
except Exception as e:
|
|
196
|
-
_print_error(f"Unexpected error: {str(e)}")
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _print_output(data: Any, format_type: str = "table"):
|
|
200
|
-
"""Print data in the specified format."""
|
|
201
|
-
print_output(data, format_type, console=console)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def _format_value(value: Any) -> str:
|
|
205
|
-
"""Deprecated: use format_value from helpers.format_value instead."""
|
|
206
|
-
return format_value(value)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def _print_json(data: Any):
|
|
210
|
-
"""Print JSON data in a nicely formatted way."""
|
|
211
|
-
_print_output(data, "json")
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def _print_error(error_msg: str):
|
|
215
|
-
"""Print error message in a nicely formatted way."""
|
|
216
|
-
console.print(Panel(f"{ERROR_PREFIX} {error_msg}", border_style=ERROR_PANEL_STYLE))
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def _print_operations(operations: List[Dict]):
|
|
220
|
-
print_operations(operations, console=console)
|
|
@@ -4,20 +4,16 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
-
from ecosystems_cli.commands.decorators import common_options
|
|
7
|
+
from ecosystems_cli.commands.decorators import common_options, override_auto_command
|
|
8
8
|
from ecosystems_cli.commands.execution import execute_api_call, update_context
|
|
9
9
|
from ecosystems_cli.commands.generator import APICommandGenerator
|
|
10
|
-
from ecosystems_cli.helpers.
|
|
10
|
+
from ecosystems_cli.helpers.build_kwargs import build_kwargs
|
|
11
|
+
from ecosystems_cli.helpers.purl_parser import apply_purl
|
|
11
12
|
|
|
12
13
|
advisories = APICommandGenerator.create_api_group("advisories")
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
if "get_advisories" in advisories.commands:
|
|
17
|
-
del advisories.commands["get_advisories"]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@advisories.command(name="get_advisories", help="list advisories")
|
|
16
|
+
@override_auto_command(advisories, "get_advisories", help="list advisories")
|
|
21
17
|
@click.option("--purl", type=str, default=None, help="Package URL (PURL). Example: pkg:npm/fsa")
|
|
22
18
|
@click.option("--ecosystem", type=str, default=None, help="Ecosystem to filter by")
|
|
23
19
|
@click.option("--package-name", type=str, default=None, help="Package to filter by")
|
|
@@ -73,37 +69,24 @@ def get_advisories(
|
|
|
73
69
|
"""
|
|
74
70
|
update_context(ctx, timeout, format, domain, mailto)
|
|
75
71
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ecosystem = parsed_ecosystem
|
|
81
|
-
if parsed_package_name:
|
|
82
|
-
package_name = parsed_package_name
|
|
72
|
+
# Explicit flags win over PURL-derived values.
|
|
73
|
+
parsed = apply_purl(purl)
|
|
74
|
+
ecosystem = ecosystem or parsed.get("ecosystem")
|
|
75
|
+
package_name = package_name or parsed.get("package_name")
|
|
83
76
|
|
|
84
77
|
# Build kwargs for the API call
|
|
85
|
-
kwargs =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if created_after is not None:
|
|
99
|
-
kwargs["created_after"] = created_after
|
|
100
|
-
if updated_after is not None:
|
|
101
|
-
kwargs["updated_after"] = updated_after
|
|
102
|
-
if sort is not None:
|
|
103
|
-
kwargs["sort"] = sort
|
|
104
|
-
if order is not None:
|
|
105
|
-
kwargs["order"] = order
|
|
106
|
-
if source is not None:
|
|
107
|
-
kwargs["source"] = source
|
|
78
|
+
kwargs = build_kwargs(
|
|
79
|
+
ecosystem=ecosystem,
|
|
80
|
+
package_name=package_name,
|
|
81
|
+
severity=severity,
|
|
82
|
+
repository_url=repository_url,
|
|
83
|
+
page=page,
|
|
84
|
+
per_page=per_page,
|
|
85
|
+
created_after=created_after,
|
|
86
|
+
updated_after=updated_after,
|
|
87
|
+
sort=sort,
|
|
88
|
+
order=order,
|
|
89
|
+
source=source,
|
|
90
|
+
)
|
|
108
91
|
|
|
109
92
|
execute_api_call(ctx, "advisories", operation_id="getAdvisories", call_args=(), call_kwargs=kwargs)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Decorators for ecosystems CLI commands."""
|
|
2
|
+
|
|
3
|
+
from functools import wraps
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ecosystems_cli.constants import (
|
|
8
|
+
DEFAULT_OUTPUT_FORMAT,
|
|
9
|
+
DEFAULT_TIMEOUT,
|
|
10
|
+
OUTPUT_FORMATS,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def common_options(f):
|
|
15
|
+
"""Decorator to add common options to commands."""
|
|
16
|
+
f = click.option(
|
|
17
|
+
"--format",
|
|
18
|
+
default=DEFAULT_OUTPUT_FORMAT,
|
|
19
|
+
type=click.Choice(OUTPUT_FORMATS),
|
|
20
|
+
help=f"Output format. Default is {DEFAULT_OUTPUT_FORMAT}.",
|
|
21
|
+
)(f)
|
|
22
|
+
f = click.option(
|
|
23
|
+
"--timeout",
|
|
24
|
+
default=DEFAULT_TIMEOUT,
|
|
25
|
+
help=f"Timeout in seconds for API requests. Default is {DEFAULT_TIMEOUT} seconds.",
|
|
26
|
+
)(f)
|
|
27
|
+
f = click.option(
|
|
28
|
+
"--domain",
|
|
29
|
+
default=None,
|
|
30
|
+
help="Override the API domain. Example: api.example.com",
|
|
31
|
+
)(f)
|
|
32
|
+
f = click.option(
|
|
33
|
+
"--mailto",
|
|
34
|
+
default=None,
|
|
35
|
+
help="Email address for polite pool access. Example: you@example.com",
|
|
36
|
+
)(f)
|
|
37
|
+
return f
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def override_auto_command(group: click.Group, name: str, **command_kwargs):
|
|
41
|
+
"""Register a custom command in place of an auto-generated one.
|
|
42
|
+
|
|
43
|
+
Drops any command already registered on ``group`` under ``name`` (the
|
|
44
|
+
auto-generated version), then registers the decorated function via
|
|
45
|
+
``group.command(name=name, **command_kwargs)``. Use this as the outermost
|
|
46
|
+
decorator instead of a bare ``del group.commands[name]`` followed by
|
|
47
|
+
``@group.command(name=name, ...)``.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def decorator(func):
|
|
51
|
+
group.commands.pop(name, None)
|
|
52
|
+
return group.command(name=name, **command_kwargs)(func)
|
|
53
|
+
|
|
54
|
+
return decorator
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def api_command(api_name: str, operation_id: str):
|
|
58
|
+
"""Decorator that wraps a command to execute an API operation.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
api_name: Name of the API (e.g., 'repos', 'packages')
|
|
62
|
+
operation_id: Operation ID to execute
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def decorator(func):
|
|
66
|
+
@common_options
|
|
67
|
+
@click.pass_context
|
|
68
|
+
@wraps(func)
|
|
69
|
+
def wrapper(ctx, timeout, format, domain, mailto, *args, **kwargs):
|
|
70
|
+
from ecosystems_cli.commands.execution import execute_api_call, update_context
|
|
71
|
+
|
|
72
|
+
update_context(ctx, timeout, format, domain, mailto)
|
|
73
|
+
execute_api_call(ctx, api_name, operation_id=operation_id, call_args=args, call_kwargs=kwargs)
|
|
74
|
+
|
|
75
|
+
return wrapper
|
|
76
|
+
|
|
77
|
+
return decorator
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Commands for the dependabot API."""
|
|
2
|
+
|
|
3
|
+
from ecosystems_cli.commands.generator import APICommandGenerator
|
|
4
|
+
from ecosystems_cli.commands.purl_options import attach_purl_option
|
|
5
|
+
|
|
6
|
+
dependabot = APICommandGenerator.create_api_group("dependabot")
|
|
7
|
+
|
|
8
|
+
# getAdvisories filters by ecosystem name and package_name (query options), so the
|
|
9
|
+
# PURL type is passed through unmapped and both parts stay optional filters.
|
|
10
|
+
attach_purl_option(
|
|
11
|
+
dependabot,
|
|
12
|
+
"get_advisories",
|
|
13
|
+
targets=("ecosystem", "package_name"),
|
|
14
|
+
map_types=False,
|
|
15
|
+
example="pkg:npm/fsa. Decomposes into --ecosystem and --package-name.",
|
|
16
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Commands for the diff API."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ecosystems_cli.commands.decorators import common_options, override_auto_command
|
|
8
|
+
from ecosystems_cli.commands.execution import update_context
|
|
9
|
+
from ecosystems_cli.commands.generator import APICommandGenerator
|
|
10
|
+
from ecosystems_cli.constants import DEFAULT_MAX_POLL_WAIT
|
|
11
|
+
from ecosystems_cli.helpers.job_polling import submit_and_poll
|
|
12
|
+
|
|
13
|
+
diff = APICommandGenerator.create_api_group("diff")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@override_auto_command(diff, "create_job", help="Submit a diff job with two URLs to compare")
|
|
17
|
+
@click.argument("url_1", required=True)
|
|
18
|
+
@click.argument("url_2", required=True)
|
|
19
|
+
@click.option(
|
|
20
|
+
"--polling-interval",
|
|
21
|
+
type=float,
|
|
22
|
+
default=None,
|
|
23
|
+
help="Polling interval in seconds. If set, the command will poll the job status until completion.",
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--max-wait",
|
|
27
|
+
type=float,
|
|
28
|
+
default=DEFAULT_MAX_POLL_WAIT,
|
|
29
|
+
help=f"Maximum seconds to poll before giving up. Default is {DEFAULT_MAX_POLL_WAIT}.",
|
|
30
|
+
)
|
|
31
|
+
@common_options
|
|
32
|
+
@click.pass_context
|
|
33
|
+
def create_job(
|
|
34
|
+
ctx,
|
|
35
|
+
timeout: int,
|
|
36
|
+
format: str,
|
|
37
|
+
domain: Optional[str],
|
|
38
|
+
mailto: Optional[str],
|
|
39
|
+
url_1: str,
|
|
40
|
+
url_2: str,
|
|
41
|
+
polling_interval: Optional[float],
|
|
42
|
+
max_wait: float,
|
|
43
|
+
):
|
|
44
|
+
"""Submit a diff job with two URLs to compare.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
ctx: Click context
|
|
48
|
+
timeout: Request timeout
|
|
49
|
+
format: Output format
|
|
50
|
+
domain: API domain
|
|
51
|
+
mailto: Email for polite pool access
|
|
52
|
+
url_1: First URL of file or zip/tar archive
|
|
53
|
+
url_2: Second URL of file or zip/tar archive
|
|
54
|
+
polling_interval: Optional polling interval in seconds
|
|
55
|
+
max_wait: Maximum seconds to poll before giving up
|
|
56
|
+
"""
|
|
57
|
+
update_context(ctx, timeout, format, domain, mailto)
|
|
58
|
+
submit_and_poll(ctx, "diff", {"url_1": url_1, "url_2": url_2}, polling_interval=polling_interval, max_wait=max_wait)
|
|
@@ -16,7 +16,14 @@ console = Console()
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def update_context(ctx, timeout: int, format: str, domain: Optional[str], mailto: Optional[str] = None):
|
|
19
|
-
"""
|
|
19
|
+
"""Merge command-level options into ``ctx.obj``.
|
|
20
|
+
|
|
21
|
+
The single context-precedence mechanism, used by both the API group callback
|
|
22
|
+
and every leaf command. An option is written only when it differs from its
|
|
23
|
+
default (i.e. was explicitly set at this level); otherwise the value already
|
|
24
|
+
in ``ctx.obj`` -- inherited from the parent context -- is left untouched.
|
|
25
|
+
The net precedence is therefore: leaf option > group option > root option >
|
|
26
|
+
default.
|
|
20
27
|
|
|
21
28
|
Args:
|
|
22
29
|
ctx: Click context
|
|
@@ -36,61 +43,69 @@ def update_context(ctx, timeout: int, format: str, domain: Optional[str], mailto
|
|
|
36
43
|
ctx.obj["mailto"] = mailto
|
|
37
44
|
|
|
38
45
|
|
|
46
|
+
def _extract_body(call_kwargs: dict, body_keys: Optional[list]) -> dict:
|
|
47
|
+
"""Pull request-body fields out of ``call_kwargs`` into a JSON body dict.
|
|
48
|
+
|
|
49
|
+
Removes each ``body_keys`` entry from ``call_kwargs`` (mutating it) so the
|
|
50
|
+
remaining kwargs map cleanly to path/query parameters. Unset values are
|
|
51
|
+
dropped; Click's repeatable options arrive as tuples and are normalized to
|
|
52
|
+
lists so they serialize as JSON arrays.
|
|
53
|
+
"""
|
|
54
|
+
body: dict = {}
|
|
55
|
+
for key in body_keys or []:
|
|
56
|
+
value = call_kwargs.pop(key, None)
|
|
57
|
+
if value is None or value == ():
|
|
58
|
+
continue
|
|
59
|
+
body[key] = list(value) if isinstance(value, tuple) else value
|
|
60
|
+
return body
|
|
61
|
+
|
|
62
|
+
|
|
39
63
|
def execute_api_call(
|
|
40
64
|
ctx,
|
|
41
65
|
api_name: str,
|
|
42
|
-
|
|
43
|
-
operation_id: Optional[str] = None,
|
|
66
|
+
operation_id: str,
|
|
44
67
|
call_args: tuple = (),
|
|
45
68
|
call_kwargs: Optional[dict] = None,
|
|
69
|
+
body_keys: Optional[list] = None,
|
|
46
70
|
):
|
|
47
71
|
"""Execute an API call with proper error handling.
|
|
48
72
|
|
|
49
73
|
Args:
|
|
50
74
|
ctx: Click context
|
|
51
75
|
api_name: Name of the API (e.g., 'repos', 'packages')
|
|
52
|
-
|
|
53
|
-
operation_id: Operation ID (for call method)
|
|
76
|
+
operation_id: Operation ID to call
|
|
54
77
|
call_args: Positional arguments for the API call
|
|
55
78
|
call_kwargs: Keyword arguments for the API call
|
|
79
|
+
body_keys: Names of kwargs that belong in the JSON request body rather
|
|
80
|
+
than the path/query parameters
|
|
56
81
|
"""
|
|
57
82
|
if call_kwargs is None:
|
|
58
83
|
call_kwargs = {}
|
|
59
84
|
|
|
85
|
+
# Separate request-body fields before path/query mapping.
|
|
86
|
+
body = _extract_body(call_kwargs, body_keys)
|
|
87
|
+
|
|
60
88
|
# Get domain with proper precedence
|
|
61
89
|
domain = get_domain_with_precedence(api_name, ctx.obj.get("domain"))
|
|
62
90
|
base_url = build_base_url(domain, api_name)
|
|
63
91
|
|
|
64
92
|
try:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
result = api_factory.call(
|
|
82
|
-
api_name,
|
|
83
|
-
operation_id,
|
|
84
|
-
path_params={},
|
|
85
|
-
query_params={},
|
|
86
|
-
timeout=ctx.obj.get("timeout", DEFAULT_TIMEOUT),
|
|
87
|
-
mailto=ctx.obj.get("mailto"),
|
|
88
|
-
base_url=base_url,
|
|
89
|
-
)
|
|
90
|
-
elif method_name:
|
|
91
|
-
raise ValueError("Direct method calls not supported")
|
|
92
|
-
else:
|
|
93
|
-
raise ValueError("Either method_name or operation_id must be provided")
|
|
93
|
+
handler = OperationHandlerFactory.get_handler(api_name)
|
|
94
|
+
path_params, query_params = handler.build_params(operation_id, call_args, call_kwargs)
|
|
95
|
+
|
|
96
|
+
call_params = {
|
|
97
|
+
"path_params": path_params,
|
|
98
|
+
"query_params": query_params,
|
|
99
|
+
"timeout": ctx.obj.get("timeout", DEFAULT_TIMEOUT),
|
|
100
|
+
"mailto": ctx.obj.get("mailto"),
|
|
101
|
+
"base_url": base_url,
|
|
102
|
+
}
|
|
103
|
+
# Only send a body when there is one, so body-less calls keep their
|
|
104
|
+
# existing call signature.
|
|
105
|
+
if body:
|
|
106
|
+
call_params["body"] = body
|
|
107
|
+
|
|
108
|
+
result = api_factory.call(api_name, operation_id, **call_params)
|
|
94
109
|
|
|
95
110
|
print_output(result, ctx.obj.get("format", DEFAULT_OUTPUT_FORMAT), console=console)
|
|
96
111
|
except EcosystemsCLIError as e:
|
|
@@ -5,9 +5,8 @@ from typing import List
|
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
|
-
from ecosystems_cli.commands.decorators import common_options
|
|
9
|
-
from ecosystems_cli.
|
|
10
|
-
from ecosystems_cli.helpers.click_params import build_click_decorators
|
|
8
|
+
from ecosystems_cli.commands.decorators import common_options
|
|
9
|
+
from ecosystems_cli.helpers.click_params import build_body_decorators, build_click_decorators
|
|
11
10
|
from ecosystems_cli.helpers.load_api_spec import load_api_spec
|
|
12
11
|
|
|
13
12
|
|
|
@@ -40,17 +39,11 @@ class APICommandGenerator:
|
|
|
40
39
|
@click.pass_context
|
|
41
40
|
def api_group(ctx, timeout, format, domain, mailto):
|
|
42
41
|
f"""Commands for the {api_name} API."""
|
|
43
|
-
|
|
42
|
+
from ecosystems_cli.commands.execution import update_context
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
mailto = resolve_context_value(ctx, "mailto", mailto, None)
|
|
49
|
-
|
|
50
|
-
ctx.obj["timeout"] = timeout
|
|
51
|
-
ctx.obj["format"] = format
|
|
52
|
-
ctx.obj["domain"] = domain
|
|
53
|
-
ctx.obj["mailto"] = mailto
|
|
44
|
+
# Same merge rule as leaf commands: a value set at this level wins;
|
|
45
|
+
# otherwise the value inherited from the parent context is kept.
|
|
46
|
+
update_context(ctx, timeout, format, domain, mailto)
|
|
54
47
|
|
|
55
48
|
api_group.name = api_name
|
|
56
49
|
return api_group
|
|
@@ -70,12 +63,13 @@ class APICommandGenerator:
|
|
|
70
63
|
command_name = APICommandGenerator.operation_id_to_command_name(operation_id)
|
|
71
64
|
description = operation.get("summary", f"Execute {operation_id}")
|
|
72
65
|
parameters = operation.get("parameters", [])
|
|
66
|
+
request_body = operation.get("requestBody")
|
|
73
67
|
|
|
74
|
-
if not parameters:
|
|
68
|
+
if not parameters and not request_body:
|
|
75
69
|
APICommandGenerator._create_simple_command(api_group, command_name, description, api_name, operation_id)
|
|
76
70
|
else:
|
|
77
71
|
APICommandGenerator._create_parameterized_command(
|
|
78
|
-
api_group, command_name, description, api_name, operation_id, parameters
|
|
72
|
+
api_group, command_name, description, api_name, operation_id, parameters, request_body
|
|
79
73
|
)
|
|
80
74
|
|
|
81
75
|
@staticmethod
|
|
@@ -90,12 +84,22 @@ class APICommandGenerator:
|
|
|
90
84
|
|
|
91
85
|
@staticmethod
|
|
92
86
|
def _create_parameterized_command(
|
|
93
|
-
api_group: click.Group,
|
|
87
|
+
api_group: click.Group,
|
|
88
|
+
command_name: str,
|
|
89
|
+
description: str,
|
|
90
|
+
api_name: str,
|
|
91
|
+
operation_id: str,
|
|
92
|
+
parameters: List[dict],
|
|
93
|
+
request_body: dict = None,
|
|
94
94
|
):
|
|
95
|
-
"""Create a command with parameters."""
|
|
95
|
+
"""Create a command with parameters and/or a JSON request body."""
|
|
96
96
|
click_decorators = APICommandGenerator._build_click_decorators(parameters)
|
|
97
|
+
body_params: List[str] = []
|
|
98
|
+
if request_body:
|
|
99
|
+
body_decorators, body_params = build_body_decorators(request_body)
|
|
100
|
+
click_decorators = click_decorators + body_decorators
|
|
97
101
|
|
|
98
|
-
def make_command(op_id):
|
|
102
|
+
def make_command(op_id, body_keys):
|
|
99
103
|
@api_group.command(name=command_name, help=description)
|
|
100
104
|
@common_options
|
|
101
105
|
@click.pass_context
|
|
@@ -103,14 +107,14 @@ class APICommandGenerator:
|
|
|
103
107
|
from ecosystems_cli.commands.execution import execute_api_call, update_context
|
|
104
108
|
|
|
105
109
|
update_context(ctx, timeout, format, domain, mailto)
|
|
106
|
-
execute_api_call(ctx, api_name, operation_id=op_id, call_args=args, call_kwargs=kwargs)
|
|
110
|
+
execute_api_call(ctx, api_name, operation_id=op_id, call_args=args, call_kwargs=kwargs, body_keys=body_keys)
|
|
107
111
|
|
|
108
112
|
for decorator in reversed(click_decorators):
|
|
109
113
|
command_impl = decorator(command_impl)
|
|
110
114
|
|
|
111
115
|
return command_impl
|
|
112
116
|
|
|
113
|
-
make_command(operation_id)
|
|
117
|
+
make_command(operation_id, body_params)
|
|
114
118
|
|
|
115
119
|
@staticmethod
|
|
116
120
|
def create_api_group(api_name: str) -> click.Group:
|