Skip to content

Commit bf3dee2

Browse files
authored
Add get_plugin method for GET /api/plugins/:name (#86)
* Add get_plugin method to fetch plugin description by name Implement get_plugin(name) on both Client and AsyncClient, calling GET /api/plugins/:name. Returns a single APIResult with name and description fields. * CHANGELOG: add get_plugin method entry
1 parent e0e3d3b commit bf3dee2

5 files changed

Lines changed: 68 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ and this project adheres to
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
- Add `get_plugin(name)` method to fetch plugin description by name
14+
([dbb4d57], [#85])
15+
1116
### Changed
1217

1318
- README: update PyPI badges and add supported Python versions
@@ -100,6 +105,7 @@ and this project adheres to
100105
[0.1.9]: https://github.com/LeakIX/LeakIXClient-Python/releases/tag/v0.1.9
101106

102107
<!-- Commit links -->
108+
[dbb4d57]: https://github.com/LeakIX/LeakIXClient-Python/commit/dbb4d57
103109
[d9e4bf8]: https://github.com/LeakIX/LeakIXClient-Python/commit/d9e4bf8
104110
[87b68f6]: https://github.com/LeakIX/LeakIXClient-Python/commit/87b68f6
105111
[591c046]: https://github.com/LeakIX/LeakIXClient-Python/commit/591c046
@@ -124,6 +130,7 @@ and this project adheres to
124130
[4dd4948]: https://github.com/LeakIX/LeakIXClient-Python/commit/4dd4948
125131

126132
<!-- PR links -->
133+
[#85]: https://github.com/LeakIX/LeakIXClient-Python/issues/85
127134
[#84]: https://github.com/LeakIX/LeakIXClient-Python/issues/84
128135
[#66]: https://github.com/LeakIX/LeakIXClient-Python/pull/66
129136
[#65]: https://github.com/LeakIX/LeakIXClient-Python/issues/65

leakix/async_client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ async def get_plugins(self) -> AbstractResponse:
135135
"""Returns the list of plugins the authenticated user has access to."""
136136
return self._parse_plugins(await self.__get("/api/plugins"))
137137

138+
async def get_plugin(self, name: str) -> AbstractResponse:
139+
"""Returns the description of a plugin by its name."""
140+
return self._parse_plugin(await self.__get(f"/api/plugins/{name}"))
141+
138142
async def get_subdomains(self, domain: str) -> AbstractResponse:
139143
"""Returns the list of subdomains for a given domain."""
140144
return self._parse_subdomains(await self.__get(f"/api/subdomains/{domain}"))

leakix/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ def _parse_plugins(response: AbstractResponse) -> AbstractResponse:
6666
response.response_json = [APIResult.from_dict(d) for d in response.json()]
6767
return response
6868

69+
@staticmethod
70+
def _parse_plugin(response: AbstractResponse) -> AbstractResponse:
71+
if response.is_success():
72+
response.response_json = APIResult.from_dict(response.json())
73+
return response
74+
6975
@staticmethod
7076
def _parse_subdomains(response: AbstractResponse) -> AbstractResponse:
7177
if response.is_success():

leakix/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ def get_plugins(self) -> AbstractResponse:
113113
url = f"{self.base_url}/api/plugins"
114114
return self._parse_plugins(self.__get(url, params=None))
115115

116+
def get_plugin(self, name: str) -> AbstractResponse:
117+
"""
118+
Returns the description of a plugin by its name.
119+
120+
The output is an `APIResult` object with `name` and `description` fields.
121+
"""
122+
url = f"{self.base_url}/api/plugins/{name}"
123+
return self._parse_plugin(self.__get(url, params=None))
124+
116125
def get_subdomains(self, domain: str) -> AbstractResponse:
117126
"""
118127
Returns the list of subdomains for a given domain.

tests/test_client.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,48 @@ def test_get_plugins_unauthorized(self, client):
220220
assert response.status_code() == 401
221221

222222

223+
class TestGetPlugin:
224+
def test_get_plugin_success(self, client_with_api_key):
225+
res_json = {
226+
"name": "GrafanaOpenPlugin",
227+
"description": "Grafana open instances",
228+
}
229+
with requests_mock.Mocker() as m:
230+
m.get(
231+
f"{client_with_api_key.base_url}/api/plugins/GrafanaOpenPlugin",
232+
json=res_json,
233+
status_code=200,
234+
)
235+
response = client_with_api_key.get_plugin("GrafanaOpenPlugin")
236+
assert response.is_success()
237+
assert response.json().name == "GrafanaOpenPlugin"
238+
assert response.json().description == "Grafana open instances"
239+
240+
def test_get_plugin_not_found(self, client):
241+
res_json = {"title": "Not Found", "description": "Plugin not found"}
242+
with requests_mock.Mocker() as m:
243+
m.get(
244+
f"{client.base_url}/api/plugins/NonExistent",
245+
json=res_json,
246+
status_code=404,
247+
)
248+
response = client.get_plugin("NonExistent")
249+
assert response.is_error()
250+
assert response.status_code() == 404
251+
252+
def test_get_plugin_unauthorized(self, client):
253+
res_json = {"error": "unauthorized"}
254+
with requests_mock.Mocker() as m:
255+
m.get(
256+
f"{client.base_url}/api/plugins/GrafanaOpenPlugin",
257+
json=res_json,
258+
status_code=401,
259+
)
260+
response = client.get_plugin("GrafanaOpenPlugin")
261+
assert response.is_error()
262+
assert response.status_code() == 401
263+
264+
223265
class TestGetSubdomains:
224266
def test_get_subdomains_success(self, client):
225267
res_json = [

0 commit comments

Comments
 (0)