From dd9ae79184bfafb4f45232c7772dace8ab3a6891 Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Thu, 11 Apr 2024 14:47:54 +0530 Subject: [PATCH 1/6] Implement teams read endpoints for user and actual user. --- grafana_client/elements/_async/user.py | 17 +++++++++++++++++ grafana_client/elements/user.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/grafana_client/elements/_async/user.py b/grafana_client/elements/_async/user.py index f0dded8..e902116 100644 --- a/grafana_client/elements/_async/user.py +++ b/grafana_client/elements/_async/user.py @@ -85,6 +85,15 @@ async def get_user_organisations(self, user_id): get_user_organisations_path = "/users/%s/orgs" % user_id return await self.client.GET(get_user_organisations_path) + async def get_user_teams(self, user_id): + """ + + :param user_id: + :return: + """ + get_user_teams_path = "/users/%s/teams" % user_id + return await self.client.GET(get_user_teams_path) + class User(Base): def __init__(self, client): @@ -145,6 +154,14 @@ async def get_actual_user_organisations(self): get_actual_user_organisations_path = "/user/orgs" return await self.client.GET(get_actual_user_organisations_path) + async def get_actual_user_teams(self): + """ + + :return: + """ + get_actual_user_teams_path = "/user/teams" + return await self.client.GET(get_actual_user_teams_path) + async def star_actual_user_dashboard(self, dashboard_id): """ diff --git a/grafana_client/elements/user.py b/grafana_client/elements/user.py index 7c1129c..e9b179d 100644 --- a/grafana_client/elements/user.py +++ b/grafana_client/elements/user.py @@ -85,6 +85,15 @@ def get_user_organisations(self, user_id): get_user_organisations_path = "/users/%s/orgs" % user_id return self.client.GET(get_user_organisations_path) + def get_user_teams(self, user_id): + """ + + :param user_id: + :return: + """ + get_user_teams_path = "/users/%s/teams" % user_id + return self.client.GET(get_user_teams_path) + class User(Base): def __init__(self, client): @@ -145,6 +154,14 @@ def get_actual_user_organisations(self): get_actual_user_organisations_path = "/user/orgs" return self.client.GET(get_actual_user_organisations_path) + def get_actual_user_teams(self): + """ + + :return: + """ + get_actual_user_teams_path = "/user/teams" + return self.client.GET(get_actual_user_teams_path) + def star_actual_user_dashboard(self, dashboard_id): """ From 0fef8564a42dae00d00b975f9d9e3eb430f73870 Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Fri, 12 Apr 2024 17:57:27 +0530 Subject: [PATCH 2/6] Add support for query parameters on endpoints. Fixes #173 (get_all_folders) --- grafana_client/client.py | 6 ++++-- grafana_client/elements/_async/folder.py | 6 +++--- grafana_client/elements/folder.py | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/grafana_client/client.py b/grafana_client/client.py index 7ee3062..e892030 100644 --- a/grafana_client/client.py +++ b/grafana_client/client.py @@ -189,7 +189,7 @@ def _extract_from_response(r, accept_empty_json): raise def __getattr__(self, item): - def __request_runner(url, json=None, data=None, headers=None, accept_empty_json=False): + def __request_runner(url, json=None, data=None, params=None, headers=None, accept_empty_json=False): __url = self._make_url(url) # Sanity checks. self._ensure_valid_json_arg(json) @@ -200,6 +200,7 @@ def __request_runner(url, json=None, data=None, headers=None, accept_empty_json= __url, json=json, data=data, + params=params, headers=headers, auth=self.auth, verify=self.verify, @@ -244,7 +245,7 @@ def __init__( self.s.headers.setdefault("Connection", "keep-alive") def __getattr__(self, item): - async def __request_runner(url, json=None, data=None, headers=None, accept_empty_json=False): + async def __request_runner(url, json=None, data=None, params=None, headers=None, accept_empty_json=False): __url = self._make_url(url) # Sanity checks. self._ensure_valid_json_arg(json) @@ -255,6 +256,7 @@ async def __request_runner(url, json=None, data=None, headers=None, accept_empty __url, json=json, data=data, + params=params, headers=headers, auth=self.auth, verify=self.verify, diff --git a/grafana_client/elements/_async/folder.py b/grafana_client/elements/_async/folder.py index e60b3d0..e92fb9b 100644 --- a/grafana_client/elements/_async/folder.py +++ b/grafana_client/elements/_async/folder.py @@ -12,10 +12,10 @@ async def get_all_folders(self, parent_uid=None): :return: """ path = "/folders" - data = {} + params = {} if parent_uid: - data["parentUid"] = parent_uid - return await self.client.GET(path, data=data) + params["parentUid"] = parent_uid + return await self.client.GET(path, params=params) async def get_folder(self, uid): """ diff --git a/grafana_client/elements/folder.py b/grafana_client/elements/folder.py index 62f2e0d..5598bba 100644 --- a/grafana_client/elements/folder.py +++ b/grafana_client/elements/folder.py @@ -12,10 +12,10 @@ def get_all_folders(self, parent_uid=None): :return: """ path = "/folders" - data = {} + params = {} if parent_uid: - data["parentUid"] = parent_uid - return self.client.GET(path, data=data) + params["parentUid"] = parent_uid + return self.client.GET(path, params=params) def get_folder(self, uid): """ From d9f7110c3998b1b61ef4707914807ea1060e2b92 Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Fri, 12 Apr 2024 18:39:13 +0530 Subject: [PATCH 3/6] Make test_grafana_client_non_json_response more permissive. Update test_grafana_client_no_verify to not break once params are added in. --- test/test_grafana_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_grafana_client.py b/test/test_grafana_client.py index e3c6f9c..38013a7 100644 --- a/test/test_grafana_client.py +++ b/test/test_grafana_client.py @@ -6,6 +6,7 @@ from grafana_client.api import GrafanaApi from grafana_client.client import ( GrafanaClientError, + GrafanaServerError, GrafanaTimeoutError, HeaderAuth, TokenAuth, @@ -95,6 +96,7 @@ def test_grafana_client_no_verify(self): auth=basic_auth, headers=None, json=None, + params=None, data=None, verify=False, timeout=5.0, @@ -160,7 +162,7 @@ def test_grafana_client_version(self, mock_get): def test_grafana_client_non_json_response(self): grafana = GrafanaApi.from_url("https://example.org/") - self.assertRaises(GrafanaClientError, lambda: grafana.connect()) + self.assertRaises((GrafanaClientError, GrafanaServerError), lambda: grafana.connect()) def test_grafana_client_204_no_content_response(self): grafana = GrafanaApi.from_url() From 4144e92b39c18b81968e4da61dbb6e9b4370389e Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Fri, 12 Apr 2024 22:02:07 +0530 Subject: [PATCH 4/6] Add missing libraryelement inclusion in the Async client. --- grafana_client/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grafana_client/api.py b/grafana_client/api.py index f9317cb..9d23ecc 100644 --- a/grafana_client/api.py +++ b/grafana_client/api.py @@ -53,6 +53,7 @@ AsyncTeams, AsyncUser, AsyncUsers, + AsyncLibraryElement, ) from .util import as_bool @@ -235,6 +236,7 @@ def __init__( self.notifications = AsyncNotifications(self.client) self.plugin = AsyncPlugin(self.client) self.serviceaccount = AsyncServiceAccount(self.client) + self.libraryelement = AsyncLibraryElement(self.client, self) self._grafana_info = None From 443194e76bef7641bcd0dc4fb55bb9adc2e0c3ca Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Fri, 12 Apr 2024 22:25:03 +0530 Subject: [PATCH 5/6] Fix import ordering. --- grafana_client/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana_client/api.py b/grafana_client/api.py index 9d23ecc..6237a75 100644 --- a/grafana_client/api.py +++ b/grafana_client/api.py @@ -42,6 +42,7 @@ AsyncDatasource, AsyncFolder, AsyncHealth, + AsyncLibraryElement, AsyncNotifications, AsyncOrganization, AsyncOrganizations, @@ -53,7 +54,6 @@ AsyncTeams, AsyncUser, AsyncUsers, - AsyncLibraryElement, ) from .util import as_bool From d341a2b08daad2e771a71adb1e1d74e19e6f6db6 Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Sun, 14 Apr 2024 07:20:36 +0530 Subject: [PATCH 6/6] Correct dashboard search endpoint, fixes #174. Incidentally add util to accept and format list-type params. --- grafana_client/elements/_async/search.py | 33 +++++++++++++++--------- grafana_client/elements/search.py | 33 +++++++++++++++--------- grafana_client/util.py | 7 +++++ 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/grafana_client/elements/_async/search.py b/grafana_client/elements/_async/search.py index d059daa..1d50e98 100644 --- a/grafana_client/elements/_async/search.py +++ b/grafana_client/elements/_async/search.py @@ -1,3 +1,5 @@ +from grafana_client.util import format_param_value + from ..base import Base @@ -12,7 +14,9 @@ async def search_dashboards( tag=None, type_=None, dashboard_ids=None, + dashboard_uids=None, folder_ids=None, + folder_uids=None, starred=None, limit=None, ): @@ -22,36 +26,41 @@ async def search_dashboards( :param tag: :param type_: :param dashboard_ids: + :param dashboard_uids: :param folder_ids: + :param folder_uids: :param starred: :param limit: :return: """ list_dashboard_path = "/search" - params = [] + params = {} if query: - params.append("query=%s" % query) + params["query"] = query if tag: - params.append("tag=%s" % tag) + params["tag"] = format_param_value(tag) if type_: - params.append("type=%s" % type_) + params["type"] = type_ if dashboard_ids: - params.append("dashboardIds=%s" % dashboard_ids) + params["dashboardIds"] = format_param_value(dashboard_ids) + + if dashboard_uids: + params["dashboardUIDs"] = format_param_value(dashboard_uids) if folder_ids: - params.append("folderIds=%s" % folder_ids) + params["folderIds"] = format_param_value(folder_ids) + + if folder_uids: + params["folderUIDs"] = format_param_value(folder_uids) if starred: - params.append("starred=%s" % starred) + params["starred"] = starred if limit: - params.append("limit=%s" % limit) - - list_dashboard_path += "?" - list_dashboard_path += "&".join(params) + params["limit"] = limit - return await self.client.GET(list_dashboard_path) + return await self.client.GET(list_dashboard_path, params=params) diff --git a/grafana_client/elements/search.py b/grafana_client/elements/search.py index 27353ac..b73347b 100644 --- a/grafana_client/elements/search.py +++ b/grafana_client/elements/search.py @@ -1,3 +1,5 @@ +from grafana_client.util import format_param_value + from .base import Base @@ -12,7 +14,9 @@ def search_dashboards( tag=None, type_=None, dashboard_ids=None, + dashboard_uids=None, folder_ids=None, + folder_uids=None, starred=None, limit=None, ): @@ -22,36 +26,41 @@ def search_dashboards( :param tag: :param type_: :param dashboard_ids: + :param dashboard_uids: :param folder_ids: + :param folder_uids: :param starred: :param limit: :return: """ list_dashboard_path = "/search" - params = [] + params = {} if query: - params.append("query=%s" % query) + params["query"] = query if tag: - params.append("tag=%s" % tag) + params["tag"] = format_param_value(tag) if type_: - params.append("type=%s" % type_) + params["type"] = type_ if dashboard_ids: - params.append("dashboardIds=%s" % dashboard_ids) + params["dashboardIds"] = format_param_value(dashboard_ids) + + if dashboard_uids: + params["dashboardUIDs"] = format_param_value(dashboard_uids) if folder_ids: - params.append("folderIds=%s" % folder_ids) + params["folderIds"] = format_param_value(folder_ids) + + if folder_uids: + params["folderUIDs"] = format_param_value(folder_uids) if starred: - params.append("starred=%s" % starred) + params["starred"] = starred if limit: - params.append("limit=%s" % limit) - - list_dashboard_path += "?" - list_dashboard_path += "&".join(params) + params["limit"] = limit - return self.client.GET(list_dashboard_path) + return self.client.GET(list_dashboard_path, params=params) diff --git a/grafana_client/util.py b/grafana_client/util.py index dde47f0..a90d061 100644 --- a/grafana_client/util.py +++ b/grafana_client/util.py @@ -39,3 +39,10 @@ def as_bool(value: str) -> bool: return _STR_BOOLEAN_MAPPING[value.lower()] except KeyError: raise ValueError(f"invalid truth value {value}") + + +def format_param_value(maybe_list): + if isinstance(maybe_list, list): + return ",".join([str(x) for x in maybe_list]) + else: + return maybe_list