diff --git a/README.md b/README.md index 89e8296..5acb6c8 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Work on API implementation still in progress. |---|---| | Admin | + | | Alerting | - | -| Annotations | - | +| Annotations | + | | Authentication | +- | | Dashboard | + | | Dashboard Versions | - | diff --git a/grafana_api/api/__init__.py b/grafana_api/api/__init__.py index 6cf5d6a..5015b85 100644 --- a/grafana_api/api/__init__.py +++ b/grafana_api/api/__init__.py @@ -7,3 +7,4 @@ from .search import Search from .user import User, Users from .team import Teams +from .annotations import Annotations diff --git a/grafana_api/api/admin.py b/grafana_api/api/admin.py index 9324511..97926e0 100644 --- a/grafana_api/api/admin.py +++ b/grafana_api/api/admin.py @@ -74,6 +74,6 @@ def pause_all_alerts(self, pause): :param pause: :return: """ - change_user_permissions = self.path + "/pause-all-alerts" + change_user_permissions = "/admin/pause-all-alerts" r = self.api.POST(change_user_permissions, json={"paused": pause}) return r diff --git a/grafana_api/api/annotations.py b/grafana_api/api/annotations.py new file mode 100644 index 0000000..539c77e --- /dev/null +++ b/grafana_api/api/annotations.py @@ -0,0 +1,177 @@ +from .base import Base + + +class Annotations(Base): + def __init__(self, api): + super(Annotations, self).__init__(api) + self.api = api + + def get_annotation( + self, + time_from=None, + time_to=None, + alert_id=None, + dashboard_id=None, + panel_id=None, + tags=None, + limit=None, + ): + + """ + :param time_from: + :param time_to: + :param alert_id: + :param dashboard_id: + :param panel_id: + :param tags: + :param limit: + :return: + """ + list_annotations_path = "/annotations" + params = [] + + if time_from: + params.append("time_from=%s" % time_from) + + if time_to: + params.append("time_to=%s" % time_to) + + if alert_id: + params.append("alertId=%s" % alert_id) + + if dashboard_id: + params.append("dashboardID=%s" % dashboard_id) + + if panel_id: + params.append("panelId=%s" % panel_id) + + if tags: + params.append("tags=%s" % tags) + + if limit: + params.append("limit=%s" % limit) + + list_annotations_path += "?" + list_annotations_path += "&".join(params) + + r = self.api.GET(list_annotations_path) + + return r + + def add_annotation( + self, + time_from=None, + time_to=None, + is_region=True, + tags=None, + text=None, + ): + + """ + :param time_from: + :param time_to: + :param is_region: + :param tags: + :param text: + :return: + """ + annotations_path = "/annotations" + payload = { + "time": time_from, + "timeEnd": time_to, + "isRegion": bool(is_region), + "tags": [tags], + "text": text + + } + + r = self.api.POST(annotations_path, json=payload) + + return r + + def add_annotation_graphite( + self, + what=None, + tags=True, + when=None, + data=None, + ): + """ + :param what: + :param tags: + :param when: + :param data: + :return: + """ + + annotations_path = "/annotations/graphite" + payload = { + "what": what, + "tags": [tags], + "when": when, + "data": data + + } + + r = self.api.POST(annotations_path, json=payload) + + return r + + def update_annotation( + self, + time_from=None, + time_to=None, + is_region=True, + tags=None, + text=None, + ): + """ + + :param time_from: + :param time_to: + :param is_region: + :param tags: + :param text: + :return: + """ + annotations_path = "/annotations" + payload = { + "time": time_from, + "timeEnd": time_to, + "isRegion": bool(is_region), + "tags": [tags], + "text": text + + } + + r = self.api.PUT(annotations_path, json=payload) + + return r + + def delete_annotations_by_region_id( + self, + region_id=None + ): + + """ + :param region_id: + :return: + """ + annotations_path = "/annotations/region/{}".format(region_id) + r = self.api.DELETE(annotations_path) + + return r + + def delete_annotations_by_id( + self, + annotations_id=None + ): + + """ + :param annotations_id: + :return: + """ + annotations_path = "/annotations/{}".format(annotations_id) + r = self.api.DELETE(annotations_path) + + return r diff --git a/grafana_api/api/folder.py b/grafana_api/api/folder.py index a2652bd..171fe14 100644 --- a/grafana_api/api/folder.py +++ b/grafana_api/api/folder.py @@ -76,12 +76,12 @@ def get_folder_by_id(self, folder_id): r = self.api.GET(path) return r - def get_folder_permissions(self): + def get_folder_permissions(self,uid): """ :return: """ - path = "/folders/%s/permissions" + path = "/folders/%s/permissions" % uid r = self.api.GET(path) return r diff --git a/grafana_api/grafana_api.py b/grafana_api/grafana_api.py index 81a9b5c..7c81439 100644 --- a/grafana_api/grafana_api.py +++ b/grafana_api/grafana_api.py @@ -94,21 +94,25 @@ def __request_runnner(url, json=None, headers=None): r = runner( __url, json=json, headers=headers, auth=self.auth, verify=self.verify ) + try: - if 500 <= r.status_code < 600: - raise GrafanaServerError( - "Server Error {0}: {1}".format( - r.status_code, r.content.decode("ascii", "replace") + if 500 <= r.status_code < 600: + raise GrafanaServerError( + "Client Error {0}: {1}".format(r.status_code, r.json()['message']) ) - ) - elif r.status_code == 400: - raise GrafanaBadInputError("Bad Input: `{0}`".format(r.text)) - elif r.status_code == 401: - raise GrafanaUnauthorizedError("Unauthorized") - elif 400 <= r.status_code < 500: - raise GrafanaClientError( - "Client Error {0}: {1}".format(r.status_code, r.text) - ) - return r.json() + elif r.status_code == 400: + raise GrafanaBadInputError("Bad Input: `{0}`".format(r.text)) + elif r.status_code == 401: + raise GrafanaUnauthorizedError("Unauthorized") + elif 400 <= r.status_code < 500: + raise GrafanaClientError( + "Client Error {0}: {1}".format(r.status_code, r.text) + ) + return r.json() + + except Exception as error: + print('Caught this error: ' + repr(error)) + + return __request_runnner diff --git a/grafana_api/grafana_face.py b/grafana_api/grafana_face.py index 36ce24e..0abb587 100644 --- a/grafana_api/grafana_face.py +++ b/grafana_api/grafana_face.py @@ -10,6 +10,7 @@ User, Users, Teams, + Annotations ) @@ -41,3 +42,4 @@ def __init__( self.user = User(self.api) self.users = Users(self.api) self.teams = Teams(self.api) + self.annotations = Annotations(self.api) diff --git a/test/api/test_admin.py b/test/api/test_admin.py new file mode 100644 index 0000000..0b2faf8 --- /dev/null +++ b/test/api/test_admin.py @@ -0,0 +1,231 @@ +import unittest + +import requests_mock + +from grafana_api.grafana_face import GrafanaFace + + +class AdminTestCase(unittest.TestCase): + def setUp(self): + self.cli = GrafanaFace( + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="http" + ) + + @requests_mock.Mocker() + def test_settings(self, m): + m.get( + "http://localhost/api/admin/settings", + json={ + "DEFAULT": { + "app_mode": "production" + }, + "analytics": { + "google_analytics_ua_id": "", + "reporting_enabled": "false" + }, + "auth.anonymous": { + "enabled": "true", + "org_name": "Main Org.", + "org_role": "Viewer" + }, + "auth.basic": { + "enabled": "false" + }, + "auth.github": { + "allow_sign_up": "false", + "allowed_domains": "", + "allowed_organizations": "", + "api_url": "https://api.github.com/user", + "auth_url": "https://github.com/login/oauth/authorize", + "client_id": "some_id", + "client_secret": "************", + "enabled": "false", + "scopes": "user:email,read:org", + "team_ids": "", + "token_url": "https://github.com/login/oauth/access_token" + }, + "auth.google": { + "allow_sign_up": "false", "allowed_domains": "", + "api_url": "https://www.googleapis.com/oauth2/v1/userinfo", + "auth_url": "https://accounts.google.com/o/oauth2/auth", + "client_id": "some_client_id", + "client_secret": "************", + "enabled": "false", + "scopes": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email", + "token_url": "https://accounts.google.com/o/oauth2/token" + }, + "auth.ldap": { + "config_file": "/etc/grafana/ldap.toml", + "enabled": "false" + }, + "auth.proxy": { + "auto_sign_up": "true", + "enabled": "false", + "header_name": "X-WEBAUTH-USER", + "header_property": "username" + }, + "dashboards.json": { + "enabled": "false", + "path": "/var/lib/grafana/dashboards" + }, + "database": { + "host": "127.0.0.1:0000", + "name": "grafana", + "password": "************", + "path": "grafana.db", + "ssl_mode": "disable", + "type": "sqlite3", + "user": "root" + }, + "emails": { + "templates_pattern": "emails/*.html", + "welcome_email_on_sign_up": "false" + }, + "log": { + "buffer_len": "10000", + "level": "Info", + "mode": "file" + }, + "log.console": { + "level": "" + }, + "log.file": { + "daily_rotate": "true", + "file_name": "", + "level": "", + "log_rotate": "true", + "max_days": "7", + "max_lines": "1000000", + "max_lines_shift": "28", + "max_size_shift": "" + }, + "paths": { + "data": "/tsdb/grafana", + "logs": "/logs/apps/grafana"}, + "security": { + "admin_password": "************", + "admin_user": "admin", + "cookie_remember_name": "grafana_remember", + "cookie_username": "grafana_user", + "disable_gravatar": "false", + "login_remember_days": "7", + "secret_key": "************" + }, + "server": { + "cert_file": "", + "cert_key": "", + "domain": "mygraf.com", + "enable_gzip": "false", + "enforce_domain": "false", + "http_addr": "127.0.0.1", + "http_port": "0000", + "protocol": "http", + "root_url": "%(protocol)s://%(domain)s:%(http_port)s/", + "router_logging": "true", + "data_proxy_logging": "true", + "static_root_path": "public" + }, + "session": { + "cookie_name": "grafana_sess", + "cookie_secure": "false", + "gc_interval_time": "", + "provider": "file", + "provider_config": "sessions", + "session_life_time": "86400" + }, + "smtp": { + "cert_file": "", + "enabled": "false", + "from_address": "admin@grafana.localhost", + "from_name": "Grafana", + "ehlo_identity": "dashboard.example.com", + "host": "localhost:25", + "key_file": "", + "password": "************", + "skip_verify": "false", + "user": "" + }, + "users": { + "allow_org_create": "true", + "allow_sign_up": "false", + "auto_assign_org": "true", + "auto_assign_org_role": "Viewer" + } + } + ) + admin = self.cli.admin.settings() + self.assertEqual(admin["users"]["allow_org_create"], "true") + + @requests_mock.Mocker() + def test_stats(self, m): + m.get( + "http://localhost/api/admin/stats", + json={ + "users": 2, + "orgs": 1, + "dashboards": 4, + "snapshots": 2, + "tags": 6, + "datasources": 1, + "playlists": 1, + "stars": 2, + "alerts": 2, + "activeUsers": 1 + } + ) + stats = self.cli.admin.stats() + self.assertEqual(len(stats), 10) + + @requests_mock.Mocker() + def test_create_user(self, m): + m.post( + "http://localhost/api/admin/users", + json={"id": 5, "message": "User created"} + ) + user = self.cli.admin.create_user({ + "name": "User", + "email": "user@graf.com", + "login": "user", + "password": "userpassword" + }) + self.assertEqual(user['message'], "User created") + + @requests_mock.Mocker() + def test_change_user_password(self, m): + m.put( + "http://localhost/api/admin/users/2/password", + json={"message": "User password updated"} + ) + user = self.cli.admin.change_user_password(user_id=2, password="password") + self.assertEqual(user['message'], "User password updated") + + @requests_mock.Mocker() + def test_change_user_permissions(self, m): + m.put( + "http://localhost/api/admin/users/2/permissions", + json={"message": "User permissions updated"} + ) + user = self.cli.admin.change_user_permissions(user_id=2, is_grafana_admin=True) + self.assertEqual(user['message'], "User permissions updated") + + @requests_mock.Mocker() + def test_delete_user(self, m): + m.delete( + "http://localhost/api/admin/users/2", + json={"message": "User deleted"} + ) + user = self.cli.admin.delete_user(user_id=2) + self.assertEqual(user['message'], "User deleted") + + @requests_mock.Mocker() + def test_pause_all_alerts(self, m): + m.post( + "http://localhost/api/admin/pause-all-alerts", + json={ + "state": "Paused", + "message": "alert paused", + "alertsAffected": 1 + } + ) + pause = self.cli.admin.pause_all_alerts(pause='True') + self.assertEqual(pause['message'], "alert paused") diff --git a/test/api/test_annotations.py b/test/api/test_annotations.py new file mode 100644 index 0000000..3299748 --- /dev/null +++ b/test/api/test_annotations.py @@ -0,0 +1,161 @@ +import unittest + +import requests_mock + +from grafana_api.grafana_face import GrafanaFace +from grafana_api.grafana_api import GrafanaServerError,GrafanaClientError,GrafanaUnauthorizedError,GrafanaBadInputError + + +class AnnotationsTestCase(unittest.TestCase): + def setUp(self): + self.cli = GrafanaFace( + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="http" + ) + + @requests_mock.Mocker() + def test_annotations(self, m): + m.get( + "http://localhost/api/annotations?time_from=1563183710618&time_to=1563185212275" + "&alertId=11&dashboardID=111&panelId=22&tags=tags-test&limit=1", + json=[ + { + "id": 80, + "alertId": 11, + "alertName": "", + "dashboardId": 111, + "panelId": 22, + "userId": 0, + "newState": "", + "prevState": "", + "created": 1563280160455, + "updated": 1563280160455, + "time": 1563156456006, + "text": "Annotation Description", + "regionId": 79, + "tags": [ + "tags-test" + ], + "login": "", + "email": "", + "avatarUrl": "", + "data": {} + }, + ] + ) + annotations = self.cli.annotations.get_annotation(time_from=1563183710618, time_to=1563185212275, alert_id=11, + dashboard_id=111, panel_id=22, tags="tags-test", limit=1) + self.assertEqual(annotations[0]["text"], "Annotation Description") + self.assertEqual(annotations[0]["alertId"], 11) + self.assertEqual(annotations[0]["dashboardId"], 111) + self.assertEqual(annotations[0]["panelId"], 22) + self.assertEqual(annotations[0]["tags"][0], "tags-test") + + self.assertEqual(len(annotations), 1) + + @requests_mock.Mocker() + def test_annotations_with_out_param(self, m): + m.get( + "http://localhost/api/annotations", + json=[ + { + "id": 80, + "alertId": 11, + "alertName": "", + "dashboardId": 111, + "panelId": 22, + "userId": 0, + "newState": "", + "prevState": "", + "created": 1563280160455, + "updated": 1563280160455, + "time": 1563156456006, + "text": "Annotation Description", + "regionId": 79, + "tags": [ + "tags-test" + ], + "login": "", + "email": "", + "avatarUrl": "", + "data": {} + }, + ] + ) + annotations = self.cli.annotations.get_annotation() + self.assertEqual(len(annotations), 1) + + @requests_mock.Mocker() + def test_delete_annotations_by_region_id(self, m): + m.delete("http://localhost/api/annotations/region/99", json={"message": "Annotation region deleted"}) + response = self.cli.annotations.delete_annotations_by_region_id(99) + self.assertEqual(response['message'], "Annotation region deleted") + + @requests_mock.Mocker() + def test_delete_annotations_by_id(self, m): + m.delete('http://localhost/api/annotations/99', json={"message": "Annotation deleted"}) + annotation = self.cli.annotations.delete_annotations_by_id(annotations_id=99) + self.assertEqual(annotation['message'], "Annotation deleted") + + @requests_mock.Mocker() + def test_delete_annotations_by_id_could_not_find(self, m): + m.delete("http://localhost/api/annotations/None", json={"message": "Could not find annotation to update"},status_code=500) + response = self.cli.annotations.delete_annotations_by_id(annotations_id=None) + self.assertRaises(GrafanaServerError) + + @requests_mock.Mocker() + def test_delete_annotations_by_id_forbidden(self, m): + m.delete("http://localhost/api/annotations/None", json={"message": "Forbidden"}, + status_code=403) + response = self.cli.annotations.delete_annotations_by_id(annotations_id=None) + self.assertRaises(GrafanaClientError) + + @requests_mock.Mocker() + def test_delete_annotations_by_id_unauthorized(self, m): + m.delete("http://localhost/api/annotations/None", json={"message": "Unauthorized"}, + status_code=401) + response = self.cli.annotations.delete_annotations_by_id(annotations_id=None) + self.assertRaises(GrafanaUnauthorizedError) + + @requests_mock.Mocker() + def test_delete_annotations_by_id_bad_input(self, m): + m.delete("http://localhost/api/annotations/None", json={"message": "Bad Input"}, + status_code=400) + response = self.cli.annotations.delete_annotations_by_id(annotations_id=None) + self.assertRaises(GrafanaBadInputError) + + + @requests_mock.Mocker() + def test_add_annotation(self, m): + m.post( + "http://localhost/api/annotations", + json={"endId": 80, "id": 79, "message": "Annotation added"}, + ) + annotation = self.cli.annotations.add_annotation(time_from=1563183710618, time_to=1563185212275 + , is_region=True, tags="tags-test", text="Test") + self.assertEqual(annotation["endId"], 80) + self.assertEqual(annotation["id"], 79) + self.assertEqual(annotation["message"], "Annotation added") + + @requests_mock.Mocker() + def test_update_annotation(self, m): + m.put( + "http://localhost/api/annotations", + json={"endId": 80, "id": 79, "message": "Annotation updated"}, + ) + annotation = self.cli.annotations.update_annotation(time_from=1563183710618, time_to=1563185212275 + , is_region=True, tags="tags-test", text="Test") + self.assertEqual(annotation["endId"], 80) + self.assertEqual(annotation["id"], 79) + self.assertEqual(annotation["message"], "Annotation updated") + + @requests_mock.Mocker() + def test_add_annotation_graphite(self, m): + m.post( + "http://localhost/api/annotations/graphite", + json={"message": "Graphite annotation added", "id": 1}, + ) + annotation = self.cli.annotations.add_annotation_graphite(what="Event - deploy", tags="deploy, production", + when=1467844481, data="Data") + + self.assertEqual(annotation["id"], 1) + self.assertEqual(annotation["message"], "Graphite annotation added") diff --git a/test/api/test_dashboard.py b/test/api/test_dashboard.py new file mode 100644 index 0000000..db0edd9 --- /dev/null +++ b/test/api/test_dashboard.py @@ -0,0 +1,208 @@ +import unittest + +import requests_mock + +from grafana_api.grafana_face import GrafanaFace + + +class DashboardTestCase(unittest.TestCase): + def setUp(self): + self.cli = GrafanaFace( + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="http" + ) + + @requests_mock.Mocker() + def test_get_dashboard(self, m): + m.get( + "http://localhost/api/dashboards/uid/cIBgcSjkk", + json={ + "dashboard": { + "id": 1, + "uid": "cIBgcSjkk", + "title": "Production Overview", + "tags": ["templated"], + "timezone": "browser", + "schemaVersion": 16, + "version": 0 + }, + "meta": { + "isStarred": 'false', + "url": "/d/cIBgcSjkk/production-overview", + "slug": "production-overview" + } + } + ) + dashboard = self.cli.dashboard.get_dashboard("cIBgcSjkk") + self.assertEqual(dashboard["dashboard"]["uid"], "cIBgcSjkk") + + @requests_mock.Mocker() + def test_update_dashboard(self, m): + m.post( + "http://localhost/api/dashboards/db", + json={ + "id": 1, + "uid": "cIBgcSjkk", + "url": "/d/cIBgcSjkk/production-overview", + "status": "success", + "version": 1, + "slug": "production-overview" + } + ) + dashboard = self.cli.dashboard.update_dashboard({ + "dashboard": { + "id": 1, + "uid": 'cIBgcSjkk', + "title": "Production Overview", + "tags": ["templated"], + "timezone": "browser", + "schemaVersion": 16, + "version": 0 + }, + "folderId": 0, + "overwrite": 'false' + }) + + self.assertEqual(dashboard["uid"], "cIBgcSjkk") + self.assertEqual(dashboard["status"], "success") + + @requests_mock.Mocker() + def test_get_home_dashboard(self, m): + m.get( + "http://localhost/api/dashboards/home", + json={ + "dashboard": { + "editable": 'false', + "hideControls": 'true', + "nav": [ + { + "enable": 'false', + "type": "timepicker" + } + ], + "style": "dark", + "tags": [], + "templating": { + "list": [ + ] + }, + "time": { + }, + "timezone": "browser", + "title": "Home", + "version": 5 + }, + "meta": { + "isHome": 'true', + "canSave": 'false', + "canEdit": 'false', + "canStar": 'false', + "url": "", + "expires": "0001-01-01T00:00:00Z", + "created": "0001-01-01T00:00:00Z" + } + } + ) + dashboard = self.cli.dashboard.get_home_dashboard() + self.assertEqual(dashboard["meta"]["isHome"], "true") + + @requests_mock.Mocker() + def test_delete_dashboard(self, m): + m.delete("http://localhost/api/dashboards/uid/cIBgcSjkk", json={"title": "Production Overview"}) + response = self.cli.dashboard.delete_dashboard("cIBgcSjkk") + self.assertEqual(response['title'], "Production Overview") + + @requests_mock.Mocker() + def test_get_dashboards_tags(self, m): + m.get( + "http://localhost/api/dashboards/tags", + json=[ + { + "term": "tag1", + "count": 1 + }, + { + "term": "tag2", + "count": 4 + } + ] + ) + tags = self.cli.dashboard.get_dashboards_tags() + self.assertEqual(len(tags), 2) + self.assertEqual(tags[0]["term"], "tag1") + + @requests_mock.Mocker() + def test_get_dashboard_permissions(self, m): + m.get( + "http://localhost/api/dashboards/id/1/permissions", + json=[ + { + "id": 1, + "dashboardId": 1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Viewer", + "permission": 1, + "permissionName": "View", + "uid": "", + "title": "", + "slug": "", + "isFolder": 'false', + "url": "" + }, + { + "id": 2, + "dashboardId": 1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Editor", + "permission": 2, + "permissionName": "Edit", + "uid": "", + "title": "", + "slug": "", + "isFolder": 'false', + "url": "" + } + ] + ) + permissions = self.cli.dashboard.get_dashboard_permissions(1) + self.assertEqual(len(permissions), 2) + self.assertEqual(permissions[0]["dashboardId"], 1) + + @requests_mock.Mocker() + def test_update_dashboard_permissions(self, m): + m.post( + "http://localhost/api/dashboards/id/1/permissions", + json={"message": "Dashboard permissions updated"} + ) + permissions = self.cli.dashboard.update_dashboard_permissions(1,{ + "items": [ + { + "role": "Viewer", + "permission": 1 + }, + { + "role": "Editor", + "permission": 2 + }, + { + "teamId": 1, + "permission": 1 + }, + { + "userId": 11, + "permission": 4 + } + ] + }) + self.assertEqual(permissions['message'], "Dashboard permissions updated") diff --git a/test/api/test_folder.py b/test/api/test_folder.py new file mode 100644 index 0000000..7e85dae --- /dev/null +++ b/test/api/test_folder.py @@ -0,0 +1,210 @@ +import unittest + +import requests_mock + +from grafana_api.grafana_face import GrafanaFace + + +class FolderTestCase(unittest.TestCase): + def setUp(self): + self.cli = GrafanaFace( + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="http" + ) + + @requests_mock.Mocker() + def test_get_all_folders(self, m): + m.get( + "http://localhost/api/folders", + json=[ + { + "id": 1, + "uid": "nErXDvCkzz", + "title": "Departmenet ABC", + "url": "/dashboards/f/nErXDvCkzz/department-abc", + "hasAcl": "false", + "canSave": "false", + "canEdit": "false", + "canAdmin": "false", + "createdBy": "admin", + "created": "2018-01-31T17:43:12+01:00", + "updatedBy": "admin", + "updated": "2018-01-31T17:43:12+01:00", + "version": 1 + } + ] + ) + folders = self.cli.folder.get_all_folders() + self.assertEqual(folders[0]["id"], 1) + self.assertEqual(len(folders), 1) + + @requests_mock.Mocker() + def test_get_folder(self, m): + m.get( + "http://localhost/api/folders/nErXDvCkzzh", + json={ + "id": 1, + "uid": "nErXDvCkzzh", + "title": "Departmenet ABC", + "url": "/dashboards/f/nErXDvCkzz/department-abc", + "hasAcl": "false", + "canSave": "false", + "canEdit": "false", + "canAdmin": "false", + "createdBy": "admin", + "created": "2018-01-31T17:43:12+01:00", + "updatedBy": "admin", + "updated": "2018-01-31T17:43:12+01:00", + "version": 1 + } + ) + folders = self.cli.folder.get_folder(uid="nErXDvCkzzh") + self.assertEqual(folders["uid"], "nErXDvCkzzh") + + @requests_mock.Mocker() + def test_create_folder(self, m): + m.post( + "http://localhost/api/folders", + json={ + "id": 1, + "uid": "nErXDvCkzz", + "title": "Departmenet ABC", + "url": "/dashboards/f/nErXDvCkzz/department-abc", + "hasAcl": "false", + "canSave": "false", + "canEdit": "false", + "canAdmin": "false", + "createdBy": "admin", + "created": "2018-01-31T17:43:12+01:00", + "updatedBy": "admin", + "updated": "2018-01-31T17:43:12+01:00", + "version": 1 + } + ) + folder = self.cli.folder.create_folder(title="Departmenet ABC", uid="nErXDvCkzz") + self.assertEqual(folder["uid"], "nErXDvCkzz") + + @requests_mock.Mocker() + def test_update_folder(self, m): + m.put( + "http://localhost/api/folders/nErXDvCkzz", + json={ + "id": 1, + "uid": "nErXDvCkzz", + "title": "Departmenet DEF", + "url": "/dashboards/f/nErXDvCkzz/department-def", + "hasAcl": "false", + "canSave": "false", + "canEdit": "false", + "canAdmin": "false", + "createdBy": "admin", + "created": "2018-01-31T17:43:12+01:00", + "updatedBy": "admin", + "updated": "2018-01-31T17:43:12+01:00", + "version": 1 + } + ) + folder = self.cli.folder.update_folder(title="Departmenet DEF", uid="nErXDvCkzz", version=1, overwrite=True) + self.assertEqual(folder["title"], "Departmenet DEF") + + @requests_mock.Mocker() + def test_get_folder_by_id(self, m): + m.get( + "http://localhost/api/folders/id/1", + json={ + "id": 1, + "uid": "nErXDvCkzz", + "title": "Departmenet ABC", + "url": "/dashboards/f/nErXDvCkzz/department-abc", + "hasAcl": "false", + "canSave": "false", + "canEdit": "false", + "canAdmin": "false", + "createdBy": "admin", + "created": "2018-01-31T17:43:12+01:00", + "updatedBy": "admin", + "updated": "2018-01-31T17:43:12+01:00", + "version": 1 + } + ) + folder = self.cli.folder.get_folder_by_id(folder_id=1) + self.assertEqual(folder["id"], 1) + + @requests_mock.Mocker() + def test_get_folder_permissions(self, m): + m.get( + "http://localhost/api/folders/nErXDvCkzz/permissions", + json=[ + { + "id": 1, + "folderId": -1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Viewer", + "permission": 1, + "permissionName": "View", + "uid": "nErXDvCkzz", + "title": "", + "slug": "", + "isFolder": "false", + "url": "" + }, + { + "id": 2, + "dashboardId": -1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Editor", + "permission": 2, + "permissionName": "Edit", + "uid": "", + "title": "", + "slug": "", + "isFolder": "false", + "url": "" + } + ] + ) + folder_permissions = self.cli.folder.get_folder_permissions(uid="nErXDvCkzz") + self.assertEqual(folder_permissions[0]["permissionName"], "View") + + @requests_mock.Mocker() + def test_update_folder_permissions(self, m): + m.post( + "http://localhost/api/folders/nErXDvCkzz/permissions", + json={"message": "Folder permissions updated"} + ) + folder = self.cli.folder.update_folder_permissions(uid="nErXDvCkzz", items=[ + { + "role": "Viewer", + "permission": 1 + }, + { + "role": "Editor", + "permission": 2 + }, + { + "teamId": 1, + "permission": 1 + }, + { + "userId": 11, + "permission": 4 + } + ]) + self.assertEqual(folder["message"], "Folder permissions updated") + + @requests_mock.Mocker() + def test_delete_folder(self, m): + m.delete("http://localhost/api/folders/nErXDvCkzz", json={"message": "Folder deleted"}) + folder = self.cli.folder.delete_folder(uid="nErXDvCkzz") + self.assertEqual(folder['message'], "Folder deleted") diff --git a/test/api/test_search.py b/test/api/test_search.py new file mode 100644 index 0000000..bb7df23 --- /dev/null +++ b/test/api/test_search.py @@ -0,0 +1,38 @@ +import unittest + +import requests_mock + +from grafana_api.grafana_face import GrafanaFace +from grafana_api.grafana_api import GrafanaServerError, GrafanaClientError, GrafanaUnauthorizedError, \ + GrafanaBadInputError + + +class AnnotationsTestCase(unittest.TestCase): + def setUp(self): + self.cli = GrafanaFace( + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="http" + ) + + @requests_mock.Mocker() + def test_search_dashboards(self, m): + m.get( + "http://localhost/api/search?folderIds=11&query=str_e&starred=false&tag=test_tag" + "&type=dash-folder&dashboardIds=163&limit=10", + json=[ + { + "id": 163, + "uid": "000000163", + "title": "Folder", + "url": "/dashboards/f/000000163/folder", + "type": "dash-folder", + "tags": [], + "isStarred": 'false', + "uri": "db/folder" + } + ] + ) + + result = self.cli.search.search_dashboards(query="str_e", folder_ids=11, starred="false", tag="test_tag", + type_="dash-folder", dashboard_ids=163, limit=10) + self.assertEqual(result[0]["id"], 163) + self.assertEqual(len(result), 1) diff --git a/test/api/test_team.py b/test/api/test_team.py index acf61ae..f0db16e 100644 --- a/test/api/test_team.py +++ b/test/api/test_team.py @@ -38,7 +38,7 @@ def test_search_teams_url_encodes_query(self, m): @requests_mock.Mocker() def test_search_teams_loads_all_pages(self, m): m.get( - "http://localhost/api/teams/search?query=team&page=1", + "http://localhost/api/teams/search?query=team&page=1&perpage=1", json={ "totalCount": 2, "teams": [ @@ -57,7 +57,7 @@ def test_search_teams_loads_all_pages(self, m): ) m.get( - "http://localhost/api/teams/search?query=team&page=2", + "http://localhost/api/teams/search?query=team&page=2&perpage=1", json={ "totalCount": 2, "teams": [ @@ -74,7 +74,7 @@ def test_search_teams_loads_all_pages(self, m): "perPage": 1, }, ) - teams = self.cli.teams.search_teams("team") + teams = self.cli.teams.search_teams("team",perpage=1) self.assertEqual(teams[0]["name"], "MyTestTeam") self.assertEqual(teams[1]["name"], "SecondTeam") self.assertEqual(len(teams), 2) diff --git a/test/test_grafana.py b/test/test_grafana.py index 86c6089..d4affad 100644 --- a/test/test_grafana.py +++ b/test/test_grafana.py @@ -71,7 +71,7 @@ def test_grafana_api_no_verify(self): def test_grafana_api_basic_auth(self): cli = GrafanaFace( - ("admin", "admin"), host="localhost", url_path_prefix="", protocol="https" + ("admin", "admin"), host="localhost", url_path_prefix="", protocol="https",port="3000" ) self.assertTrue(isinstance(cli.api.auth, requests.auth.HTTPBasicAuth))