ecosyste-ms-cli 1.3.2__py3-none-any.whl

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.
Files changed (84) hide show
  1. ecosyste_ms_cli-1.3.2.dist-info/METADATA +133 -0
  2. ecosyste_ms_cli-1.3.2.dist-info/RECORD +84 -0
  3. ecosyste_ms_cli-1.3.2.dist-info/WHEEL +5 -0
  4. ecosyste_ms_cli-1.3.2.dist-info/entry_points.txt +2 -0
  5. ecosyste_ms_cli-1.3.2.dist-info/licenses/LICENSE +21 -0
  6. ecosyste_ms_cli-1.3.2.dist-info/top_level.txt +1 -0
  7. ecosystems_cli/__init__.py +3 -0
  8. ecosystems_cli/__main__.py +4 -0
  9. ecosystems_cli/apis/__init__.py +0 -0
  10. ecosystems_cli/apis/advisories.openapi.yaml +347 -0
  11. ecosystems_cli/apis/archives.openapi.yaml +193 -0
  12. ecosystems_cli/apis/commits.openapi.yaml +391 -0
  13. ecosystems_cli/apis/dependabot.openapi.yaml +887 -0
  14. ecosystems_cli/apis/diff.openapi.yaml +90 -0
  15. ecosystems_cli/apis/docker.openapi.yaml +534 -0
  16. ecosystems_cli/apis/issues.openapi.yaml +839 -0
  17. ecosystems_cli/apis/licenses.openapi.yaml +80 -0
  18. ecosystems_cli/apis/opencollective.openapi.yaml +247 -0
  19. ecosystems_cli/apis/packages.openapi.yaml +2522 -0
  20. ecosystems_cli/apis/parser.openapi.yaml +97 -0
  21. ecosystems_cli/apis/registries.yaml +155 -0
  22. ecosystems_cli/apis/repos.openapi.yaml +1521 -0
  23. ecosystems_cli/apis/resolve.openapi.yaml +130 -0
  24. ecosystems_cli/apis/sbom.openapi.yaml +79 -0
  25. ecosystems_cli/apis/sponsors.openapi.yaml +283 -0
  26. ecosystems_cli/apis/summary.openapi.yaml +239 -0
  27. ecosystems_cli/apis/timeline.openapi.yaml +91 -0
  28. ecosystems_cli/cli.py +213 -0
  29. ecosystems_cli/commands/__init__.py +1 -0
  30. ecosystems_cli/commands/advisories.py +109 -0
  31. ecosystems_cli/commands/archives.py +5 -0
  32. ecosystems_cli/commands/commits.py +5 -0
  33. ecosystems_cli/commands/decorators.py +101 -0
  34. ecosystems_cli/commands/dependabot.py +5 -0
  35. ecosystems_cli/commands/diff.py +144 -0
  36. ecosystems_cli/commands/docker.py +5 -0
  37. ecosystems_cli/commands/execution.py +99 -0
  38. ecosystems_cli/commands/generator.py +127 -0
  39. ecosystems_cli/commands/handlers/__init__.py +45 -0
  40. ecosystems_cli/commands/handlers/advisories.py +73 -0
  41. ecosystems_cli/commands/handlers/archives.py +40 -0
  42. ecosystems_cli/commands/handlers/base.py +38 -0
  43. ecosystems_cli/commands/handlers/commits.py +76 -0
  44. ecosystems_cli/commands/handlers/default.py +40 -0
  45. ecosystems_cli/commands/handlers/dependabot.py +205 -0
  46. ecosystems_cli/commands/handlers/diff.py +72 -0
  47. ecosystems_cli/commands/handlers/docker.py +142 -0
  48. ecosystems_cli/commands/handlers/factory.py +60 -0
  49. ecosystems_cli/commands/handlers/issues.py +87 -0
  50. ecosystems_cli/commands/handlers/licenses.py +52 -0
  51. ecosystems_cli/commands/handlers/opencollective.py +86 -0
  52. ecosystems_cli/commands/handlers/packages.py +103 -0
  53. ecosystems_cli/commands/handlers/parser.py +57 -0
  54. ecosystems_cli/commands/handlers/repos.py +97 -0
  55. ecosystems_cli/commands/handlers/resolve.py +68 -0
  56. ecosystems_cli/commands/handlers/sbom.py +52 -0
  57. ecosystems_cli/commands/handlers/sponsors.py +52 -0
  58. ecosystems_cli/commands/handlers/summary.py +81 -0
  59. ecosystems_cli/commands/handlers/timeline.py +45 -0
  60. ecosystems_cli/commands/issues.py +5 -0
  61. ecosystems_cli/commands/licenses.py +135 -0
  62. ecosystems_cli/commands/mcp.py +54 -0
  63. ecosystems_cli/commands/opencollective.py +5 -0
  64. ecosystems_cli/commands/packages.py +151 -0
  65. ecosystems_cli/commands/parser.py +135 -0
  66. ecosystems_cli/commands/repos.py +5 -0
  67. ecosystems_cli/commands/resolve.py +160 -0
  68. ecosystems_cli/commands/sbom.py +135 -0
  69. ecosystems_cli/commands/sponsors.py +5 -0
  70. ecosystems_cli/commands/summary.py +5 -0
  71. ecosystems_cli/commands/timeline.py +5 -0
  72. ecosystems_cli/constants.py +92 -0
  73. ecosystems_cli/exceptions.py +149 -0
  74. ecosystems_cli/helpers/click_params.py +49 -0
  75. ecosystems_cli/helpers/flatten_dict.py +15 -0
  76. ecosystems_cli/helpers/format_value.py +30 -0
  77. ecosystems_cli/helpers/get_domain.py +68 -0
  78. ecosystems_cli/helpers/load_api_spec.py +31 -0
  79. ecosystems_cli/helpers/print_error.py +15 -0
  80. ecosystems_cli/helpers/print_operations.py +73 -0
  81. ecosystems_cli/helpers/print_output.py +183 -0
  82. ecosystems_cli/helpers/purl_parser.py +121 -0
  83. ecosystems_cli/mcp_server.py +267 -0
  84. ecosystems_cli/openapi_client.py +461 -0
@@ -0,0 +1,76 @@
1
+ """Handler for commits API operations."""
2
+
3
+ from typing import Any, Dict, List, Optional, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class CommitsOperationHandler(OperationHandler):
9
+ """Handler for commits API operations."""
10
+
11
+ # Operation parameter configuration
12
+ # Maps operation_id -> list of (api_param_name, lowercase_variants)
13
+ OPERATION_PARAMS = {
14
+ "getHost": [("hostName", ["hostname"])],
15
+ "getHostRepositories": [("hostName", ["hostname"])],
16
+ "getHostRepository": [("hostName", ["hostname"]), ("repoName", ["reponame"])],
17
+ "getRepositoryCommits": [("hostName", ["hostname"]), ("repoName", ["reponame"])],
18
+ "getHostCommitter": [("hostName", ["hostname"]), ("login", ["login"])],
19
+ }
20
+
21
+ def _extract_param(self, kwargs: dict, api_name: str, lowercase_variants: List[str]) -> Optional[str]:
22
+ """Extract parameter from kwargs, trying API name first, then lowercase variants.
23
+
24
+ Args:
25
+ kwargs: Keyword arguments dict
26
+ api_name: The API parameter name (exact case)
27
+ lowercase_variants: List of lowercase parameter name variants to try
28
+
29
+ Returns:
30
+ Parameter value if found, None otherwise
31
+ """
32
+ # Try exact API name first
33
+ if api_name in kwargs:
34
+ return kwargs.pop(api_name)
35
+
36
+ # Try lowercase variants
37
+ for variant in lowercase_variants:
38
+ if variant in kwargs:
39
+ return kwargs.pop(variant)
40
+
41
+ return None
42
+
43
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
44
+ """Build parameters for commits API operations.
45
+
46
+ Args:
47
+ operation_id: The operation identifier
48
+ args: Positional arguments
49
+ kwargs: Keyword arguments
50
+
51
+ Returns:
52
+ Tuple of (path_params, query_params)
53
+ """
54
+ path_params = {}
55
+ query_params = {}
56
+
57
+ # Get parameter configuration for this operation
58
+ param_config = self.OPERATION_PARAMS.get(operation_id, [])
59
+
60
+ # Extract path parameters from args or kwargs
61
+ for i, (api_name, lowercase_variants) in enumerate(param_config):
62
+ if i < len(args):
63
+ # Use positional argument
64
+ path_params[api_name] = args[i]
65
+ else:
66
+ # Try to extract from kwargs
67
+ value = self._extract_param(kwargs, api_name, lowercase_variants)
68
+ if value is not None:
69
+ path_params[api_name] = value
70
+
71
+ # For all operations, remaining kwargs are query parameters
72
+ for key, value in kwargs.items():
73
+ if value is not None:
74
+ query_params[key] = value
75
+
76
+ return path_params, query_params
@@ -0,0 +1,40 @@
1
+ """Default handler for standard API operations."""
2
+
3
+ from typing import Any, Dict, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class DefaultOperationHandler(OperationHandler):
9
+ """Default handler for standard API operations."""
10
+
11
+ # Configuration mapping: operation_id -> (param_type, param_name, arg_count)
12
+ OPERATION_CONFIG = {
13
+ "getProject": ("path", "id", 1),
14
+ "getList": ("path", "id", 1),
15
+ "getListProjects": ("path", "id", 1),
16
+ "getCollection": ("path", "id", 1),
17
+ "getCollectionProjects": ("path", "id", 1),
18
+ "getTopic": ("path", "slug", 1),
19
+ "getCollective": ("path", "id", 1),
20
+ "getCollectiveProjects": ("path", "slug", 1),
21
+ "lookupProject": ("query", "url", 1),
22
+ "createJob": ("query", "url", 1),
23
+ "getJob": ("path", "jobID", 1),
24
+ }
25
+
26
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
27
+ """Build parameters for standard API operations."""
28
+ all_args = list(args) + list(kwargs.values())
29
+
30
+ config = self.OPERATION_CONFIG.get(operation_id)
31
+ if config and len(all_args) >= config[2]:
32
+ param_type, param_name, _ = config
33
+ value = all_args[0]
34
+
35
+ if param_type == "path":
36
+ return {param_name: value}, {}
37
+ elif param_type == "query":
38
+ return {}, {param_name: value}
39
+
40
+ return {}, {}
@@ -0,0 +1,205 @@
1
+ """Handler for dependabot API operations."""
2
+
3
+ from typing import Any, Dict, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class DependabotOperationHandler(OperationHandler):
9
+ """Handler for dependabot API operations.
10
+
11
+ Handles parameter mapping for dependabot API operations including:
12
+ - Package operations: getPackages, getPackage, getEcosystemPackages
13
+ - Repository operations: getHost, getHostRepository, getHostRepositoryIssues
14
+ - Advisory operations: getAdvisories, getAdvisory
15
+ - Lookup operations: repositoriesLookup
16
+
17
+ Supports hierarchical path parameters like /hosts/{hostName}/repositories/{repoName}/issues/{issueNumber}
18
+ """
19
+
20
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
21
+ """Build parameters for dependabot API operations.
22
+
23
+ Args:
24
+ operation_id: The operation identifier
25
+ args: Positional arguments
26
+ kwargs: Keyword arguments
27
+
28
+ Returns:
29
+ Tuple of (path_params, query_params)
30
+ """
31
+ handlers = {
32
+ "getPackages": self._handle_query_params,
33
+ "getPackageEcosystems": self._handle_no_params,
34
+ "getEcosystemPackages": self._handle_ecosystem_packages,
35
+ "getPackage": self._handle_get_package,
36
+ "getIssuePackages": self._handle_issue_packages,
37
+ "repositoriesLookup": self._handle_repositories_lookup,
38
+ "getRegistries": self._handle_query_params,
39
+ "getHost": self._handle_get_host,
40
+ "getHostRepositories": self._handle_host_repositories,
41
+ "getHostRepository": self._handle_host_repository,
42
+ "getHostRepositoryIssues": self._handle_host_repository_issues,
43
+ "getHostRepositoryIssue": self._handle_host_repository_issue,
44
+ "getAdvisories": self._handle_query_params,
45
+ "getAdvisory": self._handle_get_advisory,
46
+ }
47
+
48
+ handler = handlers.get(operation_id, self._handle_default)
49
+ return handler(args, kwargs)
50
+
51
+ def _handle_ecosystem_packages(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
52
+ """Handle getEcosystemPackages operation parameters."""
53
+ path_params = {}
54
+ query_params = {}
55
+
56
+ ecosystem = args[0] if args else kwargs.pop("ecosystem", None)
57
+ if ecosystem:
58
+ path_params["ecosystem"] = ecosystem
59
+
60
+ # Add remaining kwargs as query parameters
61
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
62
+
63
+ return path_params, query_params
64
+
65
+ def _handle_get_package(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
66
+ """Handle getPackage operation parameters."""
67
+ path_params = {}
68
+
69
+ if len(args) >= 2:
70
+ path_params["ecosystem"] = args[0]
71
+ path_params["name"] = args[1]
72
+ else:
73
+ if "ecosystem" in kwargs:
74
+ path_params["ecosystem"] = kwargs["ecosystem"]
75
+ if "name" in kwargs:
76
+ path_params["name"] = kwargs["name"]
77
+
78
+ return path_params, {}
79
+
80
+ def _handle_issue_packages(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
81
+ """Handle getIssuePackages operation parameters."""
82
+ path_params = {}
83
+
84
+ issue_id = args[0] if args else kwargs.get("issueId")
85
+ if issue_id:
86
+ path_params["issueId"] = issue_id
87
+
88
+ return path_params, {}
89
+
90
+ def _handle_repositories_lookup(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
91
+ """Handle repositoriesLookup operation parameters."""
92
+ query_params = {}
93
+
94
+ url = args[0] if args else kwargs.get("url")
95
+ if url:
96
+ query_params["url"] = url
97
+
98
+ return {}, query_params
99
+
100
+ def _handle_get_host(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
101
+ """Handle getHost operation parameters."""
102
+ path_params = {}
103
+ query_params = {}
104
+
105
+ host_name = args[0] if args else kwargs.pop("hostName", None)
106
+ if host_name:
107
+ path_params["hostName"] = host_name
108
+
109
+ # Add remaining kwargs as query parameters
110
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
111
+
112
+ return path_params, query_params
113
+
114
+ def _handle_host_repositories(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
115
+ """Handle getHostRepositories operation parameters."""
116
+ path_params = {}
117
+ query_params = {}
118
+
119
+ host_name = args[0] if args else kwargs.pop("hostName", None)
120
+ if host_name:
121
+ path_params["hostName"] = host_name
122
+
123
+ # Add remaining kwargs as query parameters
124
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
125
+
126
+ return path_params, query_params
127
+
128
+ def _handle_host_repository(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
129
+ """Handle getHostRepository operation parameters."""
130
+ path_params = {}
131
+
132
+ if len(args) >= 2:
133
+ path_params["hostName"] = args[0]
134
+ path_params["repoName"] = args[1]
135
+ else:
136
+ if "hostName" in kwargs:
137
+ path_params["hostName"] = kwargs["hostName"]
138
+ if "repoName" in kwargs:
139
+ path_params["repoName"] = kwargs["repoName"]
140
+
141
+ return path_params, {}
142
+
143
+ def _handle_host_repository_issues(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
144
+ """Handle getHostRepositoryIssues operation parameters."""
145
+ path_params = {}
146
+ query_params = {}
147
+
148
+ if len(args) >= 2:
149
+ path_params["hostName"] = args[0]
150
+ path_params["repoName"] = args[1]
151
+ else:
152
+ if "hostName" in kwargs:
153
+ path_params["hostName"] = kwargs.pop("hostName")
154
+ if "repoName" in kwargs:
155
+ path_params["repoName"] = kwargs.pop("repoName")
156
+
157
+ # Add remaining kwargs as query parameters
158
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
159
+
160
+ return path_params, query_params
161
+
162
+ def _handle_host_repository_issue(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
163
+ """Handle getHostRepositoryIssue operation parameters."""
164
+ path_params = {}
165
+
166
+ if len(args) >= 3:
167
+ path_params["hostName"] = args[0]
168
+ path_params["repoName"] = args[1]
169
+ path_params["issueNumber"] = args[2]
170
+ else:
171
+ if "hostName" in kwargs:
172
+ path_params["hostName"] = kwargs["hostName"]
173
+ if "repoName" in kwargs:
174
+ path_params["repoName"] = kwargs["repoName"]
175
+ if "issueNumber" in kwargs:
176
+ path_params["issueNumber"] = kwargs["issueNumber"]
177
+
178
+ return path_params, {}
179
+
180
+ def _handle_get_advisory(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
181
+ """Handle getAdvisory operation parameters."""
182
+ path_params = {}
183
+ query_params = {}
184
+
185
+ advisory_id = args[0] if args else kwargs.pop("advisoryId", None)
186
+ if advisory_id:
187
+ path_params["advisoryId"] = advisory_id
188
+
189
+ # Add remaining kwargs as query parameters
190
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
191
+
192
+ return path_params, query_params
193
+
194
+ def _handle_query_params(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
195
+ """Handle operations that only have query parameters."""
196
+ query_params = {k: v for k, v in kwargs.items() if v is not None}
197
+ return {}, query_params
198
+
199
+ def _handle_no_params(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
200
+ """Handle operations with no parameters."""
201
+ return {}, {}
202
+
203
+ def _handle_default(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
204
+ """Default handler for unknown operations."""
205
+ return {}, {}
@@ -0,0 +1,72 @@
1
+ """Handler for diff API operations."""
2
+
3
+ from typing import Any, Dict, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class DiffOperationHandler(OperationHandler):
9
+ """Handler for diff API operations.
10
+
11
+ Handles parameter mapping for diff API operations including:
12
+ - createJob: Submit diff job with two URLs to compare
13
+ - getJob: Get job status and results by job ID
14
+ """
15
+
16
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
17
+ """Build parameters for diff API operations.
18
+
19
+ Args:
20
+ operation_id: The operation identifier
21
+ args: Positional arguments
22
+ kwargs: Keyword arguments
23
+
24
+ Returns:
25
+ Tuple of (path_params, query_params)
26
+ """
27
+ handlers = {
28
+ "createJob": self._handle_create_job,
29
+ "getJob": self._handle_get_job,
30
+ }
31
+
32
+ handler = handlers.get(operation_id, self._handle_default)
33
+ return handler(args, kwargs)
34
+
35
+ def _handle_create_job(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
36
+ """Handle createJob operation parameters.
37
+
38
+ Requires two URLs to compare. Can be provided as:
39
+ - Positional arguments: diff create-job url1 url2
40
+ - Named arguments: diff create-job --url-1 url1 --url-2 url2
41
+ """
42
+ query_params = {}
43
+
44
+ if len(args) >= 2:
45
+ query_params["url_1"] = args[0]
46
+ query_params["url_2"] = args[1]
47
+ else:
48
+ # Try to get from kwargs
49
+ url_1 = kwargs.get("url_1")
50
+ url_2 = kwargs.get("url_2")
51
+
52
+ if url_1:
53
+ query_params["url_1"] = url_1
54
+ if url_2:
55
+ query_params["url_2"] = url_2
56
+
57
+ # Note: API validation will handle missing required parameters
58
+ return {}, query_params
59
+
60
+ def _handle_get_job(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
61
+ """Handle getJob operation parameters."""
62
+ path_params = {}
63
+
64
+ job_id = args[0] if args else kwargs.get("job_id")
65
+ if job_id:
66
+ path_params["jobID"] = job_id
67
+
68
+ return path_params, {}
69
+
70
+ def _handle_default(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
71
+ """Default handler for unknown operations."""
72
+ return {}, {}
@@ -0,0 +1,142 @@
1
+ """Handler for docker API operations."""
2
+
3
+ from typing import Any, Dict, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class DockerOperationHandler(OperationHandler):
9
+ """Handler for docker API operations."""
10
+
11
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
12
+ """Build parameters for docker API operations.
13
+
14
+ Args:
15
+ operation_id: The operation identifier
16
+ args: Positional arguments
17
+ kwargs: Keyword arguments
18
+
19
+ Returns:
20
+ Tuple of (path_params, query_params)
21
+ """
22
+ handlers = {
23
+ "getPackages": self._handle_query_params,
24
+ "getPackage": self._handle_get_package,
25
+ "getPackageVersions": self._handle_get_package_versions,
26
+ "getPackageVersion": self._handle_get_package_version,
27
+ "usage": self._handle_no_params,
28
+ "usageEcosystem": self._handle_usage_ecosystem,
29
+ "usagePackage": self._handle_usage_package,
30
+ "getDistros": self._handle_query_params,
31
+ "getDistro": self._handle_get_distro,
32
+ "getDistroVersions": self._handle_get_distro_versions,
33
+ }
34
+
35
+ handler = handlers.get(operation_id, self._handle_default)
36
+ return handler(args, kwargs)
37
+
38
+ def _handle_get_package(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
39
+ """Handle getPackage operation parameters."""
40
+ path_params = {}
41
+
42
+ # Click normalizes parameter names to lowercase, so check both
43
+ package_name = args[0] if args else (kwargs.get("packageName") or kwargs.get("packagename"))
44
+ if package_name:
45
+ path_params["packageName"] = package_name
46
+
47
+ return path_params, {}
48
+
49
+ def _handle_get_package_versions(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
50
+ """Handle getPackageVersions operation parameters."""
51
+ path_params = {}
52
+ query_params = {}
53
+
54
+ # Click normalizes parameter names to lowercase
55
+ package_name = args[0] if args else (kwargs.pop("packageName", None) or kwargs.pop("packagename", None))
56
+ if package_name:
57
+ path_params["packageName"] = package_name
58
+
59
+ # Add remaining kwargs as query parameters
60
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
61
+
62
+ return path_params, query_params
63
+
64
+ def _handle_get_package_version(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
65
+ """Handle getPackageVersion operation parameters."""
66
+ path_params = {}
67
+
68
+ if len(args) >= 2:
69
+ path_params["packageName"] = args[0]
70
+ path_params["versionNumber"] = args[1]
71
+ else:
72
+ # Click normalizes parameter names to lowercase
73
+ if "packageName" in kwargs or "packagename" in kwargs:
74
+ path_params["packageName"] = kwargs.get("packageName") or kwargs.get("packagename")
75
+ if "versionNumber" in kwargs or "versionnumber" in kwargs:
76
+ path_params["versionNumber"] = kwargs.get("versionNumber") or kwargs.get("versionnumber")
77
+
78
+ return path_params, {}
79
+
80
+ def _handle_usage_ecosystem(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
81
+ """Handle usageEcosystem operation parameters."""
82
+ path_params = {}
83
+
84
+ ecosystem = args[0] if args else kwargs.get("ecosystem")
85
+ if ecosystem:
86
+ path_params["ecosystem"] = ecosystem
87
+
88
+ return path_params, {}
89
+
90
+ def _handle_usage_package(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
91
+ """Handle usagePackage operation parameters."""
92
+ path_params = {}
93
+
94
+ if len(args) >= 2:
95
+ path_params["ecosystem"] = args[0]
96
+ path_params["package"] = args[1]
97
+ else:
98
+ if "ecosystem" in kwargs:
99
+ path_params["ecosystem"] = kwargs["ecosystem"]
100
+ if "package" in kwargs:
101
+ path_params["package"] = kwargs["package"]
102
+
103
+ return path_params, {}
104
+
105
+ def _handle_get_distro(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
106
+ """Handle getDistro operation parameters."""
107
+ path_params = {}
108
+
109
+ # Click normalizes parameter names to lowercase, so check both
110
+ slug = args[0] if args else (kwargs.get("slug"))
111
+ if slug:
112
+ path_params["slug"] = slug
113
+
114
+ return path_params, {}
115
+
116
+ def _handle_get_distro_versions(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
117
+ """Handle getDistroVersions operation parameters."""
118
+ path_params = {}
119
+ query_params = {}
120
+
121
+ # Click normalizes parameter names to lowercase
122
+ slug = args[0] if args else kwargs.pop("slug", None)
123
+ if slug:
124
+ path_params["slug"] = slug
125
+
126
+ # Add remaining kwargs as query parameters
127
+ query_params.update({k: v for k, v in kwargs.items() if v is not None})
128
+
129
+ return path_params, query_params
130
+
131
+ def _handle_query_params(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
132
+ """Handle operations that only have query parameters."""
133
+ query_params = {k: v for k, v in kwargs.items() if v is not None}
134
+ return {}, query_params
135
+
136
+ def _handle_no_params(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
137
+ """Handle operations with no parameters."""
138
+ return {}, {}
139
+
140
+ def _handle_default(self, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
141
+ """Default handler for unknown operations."""
142
+ return {}, {}
@@ -0,0 +1,60 @@
1
+ """Factory for creating operation handlers."""
2
+
3
+ from typing import Dict, Type
4
+
5
+ from .advisories import AdvisoriesOperationHandler
6
+ from .archives import ArchivesOperationHandler
7
+ from .base import OperationHandler
8
+ from .commits import CommitsOperationHandler
9
+ from .default import DefaultOperationHandler
10
+ from .dependabot import DependabotOperationHandler
11
+ from .diff import DiffOperationHandler
12
+ from .docker import DockerOperationHandler
13
+ from .issues import IssuesOperationHandler
14
+ from .licenses import LicensesOperationHandler
15
+ from .opencollective import OpenCollectiveOperationHandler
16
+ from .packages import PackagesOperationHandler
17
+ from .parser import ParserOperationHandler
18
+ from .repos import ReposOperationHandler
19
+ from .resolve import ResolveOperationHandler
20
+ from .sbom import SbomOperationHandler
21
+ from .sponsors import SponsorsOperationHandler
22
+ from .summary import SummaryOperationHandler
23
+ from .timeline import TimelineOperationHandler
24
+
25
+
26
+ class OperationHandlerFactory:
27
+ """Factory for creating operation handlers."""
28
+
29
+ _handlers: Dict[str, Type[OperationHandler]] = {
30
+ "advisories": AdvisoriesOperationHandler,
31
+ "archives": ArchivesOperationHandler,
32
+ "commits": CommitsOperationHandler,
33
+ "dependabot": DependabotOperationHandler,
34
+ "diff": DiffOperationHandler,
35
+ "docker": DockerOperationHandler,
36
+ "issues": IssuesOperationHandler,
37
+ "licenses": LicensesOperationHandler,
38
+ "opencollective": OpenCollectiveOperationHandler,
39
+ "packages": PackagesOperationHandler,
40
+ "parser": ParserOperationHandler,
41
+ "repos": ReposOperationHandler,
42
+ "resolve": ResolveOperationHandler,
43
+ "sbom": SbomOperationHandler,
44
+ "sponsors": SponsorsOperationHandler,
45
+ "summary": SummaryOperationHandler,
46
+ "timeline": TimelineOperationHandler,
47
+ }
48
+
49
+ @classmethod
50
+ def get_handler(cls, api_name: str) -> OperationHandler:
51
+ """Get the appropriate handler for an API.
52
+
53
+ Args:
54
+ api_name: Name of the API
55
+
56
+ Returns:
57
+ OperationHandler instance for the API
58
+ """
59
+ handler_class = cls._handlers.get(api_name, DefaultOperationHandler)
60
+ return handler_class()
@@ -0,0 +1,87 @@
1
+ """Handler for issues API operations."""
2
+
3
+ from typing import Any, Dict, List, Optional, Tuple
4
+
5
+ from .base import OperationHandler
6
+
7
+
8
+ class IssuesOperationHandler(OperationHandler):
9
+ """Handler for issues API operations."""
10
+
11
+ # Operation parameter configuration
12
+ # Maps operation_id -> list of (api_param_name, lowercase_variants)
13
+ OPERATION_PARAMS = {
14
+ "getHost": [("hostName", ["hostname"])],
15
+ "getHostRepositories": [("hostName", ["hostname"])],
16
+ "getHostRepository": [("hostName", ["hostname"]), ("repoName", ["reponame"])],
17
+ "getHostRepositoryIssues": [("hostName", ["hostname"]), ("repoName", ["reponame"])],
18
+ "getHostRepositoryIssue": [
19
+ ("hostName", ["hostname"]),
20
+ ("repoName", ["reponame"]),
21
+ ("issueNumber", ["issuenumber", "issue_number"]),
22
+ ],
23
+ "getHostRepositoryLabels": [("hostName", ["hostname"]), ("repoName", ["reponame"])],
24
+ "getHostOwners": [("hostName", ["hostname"])],
25
+ "getHostOwner": [("hostName", ["hostname"]), ("ownerName", ["ownername"])],
26
+ "getHostOwnerMaintainers": [("hostName", ["hostname"]), ("ownerName", ["ownername"])],
27
+ "getHostAuthors": [("hostName", ["hostname"])],
28
+ "getHostAuthor": [("hostName", ["hostname"]), ("authorName", ["authorname"])],
29
+ "getJob": [("jobId", ["jobid"])],
30
+ }
31
+
32
+ def _extract_param(self, kwargs: dict, api_name: str, lowercase_variants: List[str]) -> Optional[str]:
33
+ """Extract parameter from kwargs, trying API name first, then lowercase variants.
34
+
35
+ Args:
36
+ kwargs: Keyword arguments dict
37
+ api_name: The API parameter name (exact case)
38
+ lowercase_variants: List of lowercase parameter name variants to try
39
+
40
+ Returns:
41
+ Parameter value if found, None otherwise
42
+ """
43
+ # Try exact API name first
44
+ if api_name in kwargs:
45
+ return kwargs.pop(api_name)
46
+
47
+ # Try lowercase variants
48
+ for variant in lowercase_variants:
49
+ if variant in kwargs:
50
+ return kwargs.pop(variant)
51
+
52
+ return None
53
+
54
+ def build_params(self, operation_id: str, args: tuple, kwargs: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]:
55
+ """Build parameters for issues API operations.
56
+
57
+ Args:
58
+ operation_id: The operation identifier
59
+ args: Positional arguments
60
+ kwargs: Keyword arguments
61
+
62
+ Returns:
63
+ Tuple of (path_params, query_params)
64
+ """
65
+ path_params = {}
66
+ query_params = {}
67
+
68
+ # Get parameter configuration for this operation
69
+ param_config = self.OPERATION_PARAMS.get(operation_id, [])
70
+
71
+ # Extract path parameters from args or kwargs
72
+ for i, (api_name, lowercase_variants) in enumerate(param_config):
73
+ if i < len(args):
74
+ # Use positional argument
75
+ path_params[api_name] = args[i]
76
+ else:
77
+ # Try to extract from kwargs
78
+ value = self._extract_param(kwargs, api_name, lowercase_variants)
79
+ if value is not None:
80
+ path_params[api_name] = value
81
+
82
+ # For all operations, remaining kwargs are query parameters
83
+ for key, value in kwargs.items():
84
+ if value is not None:
85
+ query_params[key] = value
86
+
87
+ return path_params, query_params