From b90c1c38bc230e922da4e8522aeb8855551a4b3d Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Fri, 30 Nov 2018 09:36:48 +0100 Subject: [PATCH 1/8] example script --- .../cleanupStaleDashboardsAndPermissions.py | 99 +++++++++++++++++++ teamscale_client/client.py | 23 +++++ 2 files changed, 122 insertions(+) create mode 100644 examples/cleanupStaleDashboardsAndPermissions.py diff --git a/examples/cleanupStaleDashboardsAndPermissions.py b/examples/cleanupStaleDashboardsAndPermissions.py new file mode 100644 index 0000000..6ec3489 --- /dev/null +++ b/examples/cleanupStaleDashboardsAndPermissions.py @@ -0,0 +1,99 @@ +# + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import json +import requests + +import datetime +import os +import sys +import re + +from teamscale_client import TeamscaleClient + + +TEAMSCALE_URL = "http://localhost:8080" + +USERNAME = "build" +ACCESS_TOKEN = "ide-access-token" + + +def confirm(prompt=None, resp=False): + """prompts for yes or no response from the user. Returns True for yes and + False for no. + + 'resp' should be set to the default value assumed by the caller when + user simply types ENTER. + + >>> confirm(prompt='Create Directory?', resp=True) + Create Directory? [y]|n: + True + >>> confirm(prompt='Create Directory?', resp=False) + Create Directory? [n]|y: + False + >>> confirm(prompt='Create Directory?', resp=False) + Create Directory? [n]|y: y + True + + """ + + if prompt is None: + prompt = 'Confirm' + + if resp: + prompt = '%s [%s]|%s: ' % (prompt, 'y', 'n') + else: + prompt = '%s [%s]|%s: ' % (prompt, 'n', 'y') + + while True: + ans = input(prompt) + if not ans: + return resp + if ans not in ['y', 'Y', 'n', 'N']: + print('please enter y or n.') + continue + if ans == 'y' or ans == 'Y': + return True + if ans == 'n' or ans == 'N': + return False + + +def determine_stale_dashboards(active_projects, client): + """Determines dashboards that have no reference to active projects. + + Args: + active_projects: Set of active projects + client: TeamscaleClient + + Returns: + Set of dashboard ids that do not refer to any active project. + """ + stale_dashboards=[] + for dashboard in client.get_all_dashboard_details(): + dashboard_name = '%s/%s' % (dashboard['owner'], dashboard['name']) + referenced_projects = set(re.findall('"project": "([a-zA-Z0-9_]+)"', dashboard['descriptorJSON'])) + if active_projects.isdisjoint(referenced_projects): + print("Dashboard %s only references projects %s (none of which is active anymore)" + % (dashboard_name, referenced_projects)) + stale_dashboards.append(dashboard_name) + return stale_dashboards + + +def main(): + client = TeamscaleClient(TEAMSCALE_URL, USERNAME, ACCESS_TOKEN, "") + active_projects = set([x.id for x in client.get_projects()]) + print('active project IDs:', active_projects) + + dashboards_to_be_removed = determine_stale_dashboards(active_projects, client) + + for dashboard in dashboards_to_be_removed: + if confirm('really delete dashboard %s ?'%dashboard): + print(client.delete_dashboard(dashboard)) + + +if __name__ == '__main__': + main() diff --git a/teamscale_client/client.py b/teamscale_client/client.py index e490eed..71bab6b 100644 --- a/teamscale_client/client.py +++ b/teamscale_client/client.py @@ -721,3 +721,26 @@ def get_architectures(self): if response.status_code != 200: raise ServiceError("ERROR: GET {url}: {r.status_code}:{r.text}".format(url=service_url, r=response)) return [architecture_overview['uniformPath'] for architecture_overview in response.json()] + + def get_all_dashboard_details(self): + """Returns all dashboards with detail info. + + Returns: + list of dashboard objects + """ + service_url = self.get_global_service_url("dashboards") + + response = self.get(service_url, parameters={'detail':True}) + + if response.status_code != 200: + raise ServiceError("ERROR: GET {url}: {r.status_code}:{r.text}".format(url=service_url, r=response)) + return json.loads(response.content) + + def delete_dashboard(self, dashboard_name): + """Deletes the dashboard with the given name + + Returns: + response + """ + service_url = self.get_global_service_url("dashboards") + return self.delete(service_url+ dashboard_name) From dcea2b17f2efb67658c496698dee80b2d704bfe5 Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Wed, 19 Dec 2018 14:42:16 +0100 Subject: [PATCH 2/8] corrected filename --- ...StaleDashboardsAndPermissions.py => cleanupStaleDashboards.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{cleanupStaleDashboardsAndPermissions.py => cleanupStaleDashboards.py} (100%) diff --git a/examples/cleanupStaleDashboardsAndPermissions.py b/examples/cleanupStaleDashboards.py similarity index 100% rename from examples/cleanupStaleDashboardsAndPermissions.py rename to examples/cleanupStaleDashboards.py From 98efc5fb0bf8c52ceaf6d1932494e854bd9a8e4a Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 07:14:40 +0100 Subject: [PATCH 3/8] tests --- tests/teamscale_client_test.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/teamscale_client_test.py b/tests/teamscale_client_test.py index 57a6fef..bc1e060 100644 --- a/tests/teamscale_client_test.py +++ b/tests/teamscale_client_test.py @@ -214,4 +214,20 @@ def test_add_issue_metric(): responses.add(responses.PUT, get_project_service_mock('issue-metrics'), body='{"message": "success"}', status=200) get_client().add_issue_metric("example/foo", "instate(status=YELLOW) > 2d") - assert "YELLOW" in responses.calls[1].request.body.decode() \ No newline at end of file + assert "YELLOW" in responses.calls[1].request.body.decode() + +@responses.activate +def test_get_all_dashboard_details(): + """Tests query of all dashboard details""" + responses.add(responses.GET, get_global_service_mock('dashboards'), + body='[{"name":"new dashboard","owner":"gib","descriptorJSON":"{\"widgets\":[{\"widget-id\":\"commit-chart\",\"position\":{\"x\":0,\"y\":0,\"width\":12,\"height\":6},\"Title\":\"Commit Chart\",\"Path\":{\"project\":\"myProject\",\"path\":\"\",\"hiddenInWidgetTitle\":false},\"Trend\":{\"type\":\"timespan\",\"value\":0},\"Zooming\":true,\"Hide Y-Axes\":false,\"Baselines\":true,\"Action menu\":false,\"Min. commits per developer\":0,\"Order\":\"by time\"}]}","author":"gib","comment":"","canRead":false,"canWrite":false}]', status=200) + dashboards = get_client().get_all_dashboard_details() + assert dashboards[0]['name'] == 'new dashboard' + +@responses.activate +def test_delete_dashboard(): + """Test deletion of dashboards""" + responses.add(responses.DELETE, get_project_service_mock('dashboards'), + body=SUCCESS, status=200) + resp = get_client().delete_baseline("some dashboard name") + assert resp.text == SUCCESS From 81e9e3e37c00637a153a2a0ccea338eb75812ab3 Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 07:21:22 +0100 Subject: [PATCH 4/8] fix test? --- tests/teamscale_client_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/teamscale_client_test.py b/tests/teamscale_client_test.py index bc1e060..6370de1 100644 --- a/tests/teamscale_client_test.py +++ b/tests/teamscale_client_test.py @@ -220,7 +220,7 @@ def test_add_issue_metric(): def test_get_all_dashboard_details(): """Tests query of all dashboard details""" responses.add(responses.GET, get_global_service_mock('dashboards'), - body='[{"name":"new dashboard","owner":"gib","descriptorJSON":"{\"widgets\":[{\"widget-id\":\"commit-chart\",\"position\":{\"x\":0,\"y\":0,\"width\":12,\"height\":6},\"Title\":\"Commit Chart\",\"Path\":{\"project\":\"myProject\",\"path\":\"\",\"hiddenInWidgetTitle\":false},\"Trend\":{\"type\":\"timespan\",\"value\":0},\"Zooming\":true,\"Hide Y-Axes\":false,\"Baselines\":true,\"Action menu\":false,\"Min. commits per developer\":0,\"Order\":\"by time\"}]}","author":"gib","comment":"","canRead":false,"canWrite":false}]', status=200) + body='[{"name":"new dashboard","owner":"gib","descriptorJSON":"{\\"widgets\":[{\\"widget-id\\":\\"commit-chart\\",\\"position\\":{\\"x\\":0,\\"y\\":0,\\"width\\":12,\\"height\\":6},\\"Title\\":\\"Commit Chart\\",\\"Path\\":{\\"project\\":\\"myProject\\",\\"path\\":\\"\\",\\"hiddenInWidgetTitle\\":false},\\"Trend\\":{\\"type\\":\\"timespan\\",\\"value\\":0},\\"Zooming\\":true,\\"Hide Y-Axes\\":false,\\"Baselines\\":true,\\"Action menu\\":false,\\"Min. commits per developer\\":0,\\"Order\\":\\"by time\\"}]}","author":"gib","comment":"","canRead":false,"canWrite":false}]', status=200) dashboards = get_client().get_all_dashboard_details() assert dashboards[0]['name'] == 'new dashboard' From 1d7a14f2344e09080d22dd8f4563959106c30d0f Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 07:24:57 +0100 Subject: [PATCH 5/8] fix next test? --- tests/teamscale_client_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/teamscale_client_test.py b/tests/teamscale_client_test.py index 6370de1..b20b9f4 100644 --- a/tests/teamscale_client_test.py +++ b/tests/teamscale_client_test.py @@ -229,5 +229,5 @@ def test_delete_dashboard(): """Test deletion of dashboards""" responses.add(responses.DELETE, get_project_service_mock('dashboards'), body=SUCCESS, status=200) - resp = get_client().delete_baseline("some dashboard name") + resp = get_client().delete_dashboard("some dashboard name") assert resp.text == SUCCESS From 519dbeb19681bff50761899a79a22be3d4b61d90 Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 14:54:08 +0100 Subject: [PATCH 6/8] fixed tests --- tests/teamscale_client_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/teamscale_client_test.py b/tests/teamscale_client_test.py index b20b9f4..47f54f9 100644 --- a/tests/teamscale_client_test.py +++ b/tests/teamscale_client_test.py @@ -220,14 +220,14 @@ def test_add_issue_metric(): def test_get_all_dashboard_details(): """Tests query of all dashboard details""" responses.add(responses.GET, get_global_service_mock('dashboards'), - body='[{"name":"new dashboard","owner":"gib","descriptorJSON":"{\\"widgets\":[{\\"widget-id\\":\\"commit-chart\\",\\"position\\":{\\"x\\":0,\\"y\\":0,\\"width\\":12,\\"height\\":6},\\"Title\\":\\"Commit Chart\\",\\"Path\\":{\\"project\\":\\"myProject\\",\\"path\\":\\"\\",\\"hiddenInWidgetTitle\\":false},\\"Trend\\":{\\"type\\":\\"timespan\\",\\"value\\":0},\\"Zooming\\":true,\\"Hide Y-Axes\\":false,\\"Baselines\\":true,\\"Action menu\\":false,\\"Min. commits per developer\\":0,\\"Order\\":\\"by time\\"}]}","author":"gib","comment":"","canRead":false,"canWrite":false}]', status=200) + body='[{"name":"new dashboard","owner":"gib","descriptorJSON":"{\\"widgets\\":[{\\"widget-id\\":\\"commit-chart\\",\\"position\\":{\\"x\\":0,\\"y\\":0,\\"width\\":12,\\"height\\":6},\\"Title\\":\\"Commit Chart\\",\\"Path\\":{\\"project\\":\\"myProject\\",\\"path\\":\\"\\",\\"hiddenInWidgetTitle\\":false},\\"Trend\\":{\\"type\\":\\"timespan\\",\\"value\\":0},\\"Zooming\\":true,\\"Hide Y-Axes\\":false,\\"Baselines\\":true,\\"Action menu\\":false,\\"Min. commits per developer\\":0,\\"Order\\":\\"by time\\"}]}","author":"gib","comment":"","canRead":false,"canWrite":false}]', status=200) dashboards = get_client().get_all_dashboard_details() assert dashboards[0]['name'] == 'new dashboard' @responses.activate def test_delete_dashboard(): """Test deletion of dashboards""" - responses.add(responses.DELETE, get_project_service_mock('dashboards'), + responses.add(responses.DELETE, get_global_service_mock('dashboards'), body=SUCCESS, status=200) resp = get_client().delete_dashboard("some dashboard name") assert resp.text == SUCCESS From 0b40f4c198ca95cc0b5d73173a52eed21da48e27 Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 14:57:15 +0100 Subject: [PATCH 7/8] added PyCharm reference --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7df346f..869e55b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Install via: pip install teamscale-client # Development +We suggest the [PyCcharm](https://www.jetbrains.com/pycharm/) IDE for development in this project. We are happy to add additional service calls to the client. Please make sure you include a test, if you add a service call. To run them use: python setup.py test From 40bea4ff4dff93347a7060b992deb4473c71dc22 Mon Sep 17 00:00:00 2001 From: Alex von Rhein Date: Thu, 20 Dec 2018 14:59:57 +0100 Subject: [PATCH 8/8] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 869e55b..216f60c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Install via: pip install teamscale-client # Development -We suggest the [PyCcharm](https://www.jetbrains.com/pycharm/) IDE for development in this project. +We suggest the [PyCharm](https://www.jetbrains.com/pycharm/) IDE for development in this project. We are happy to add additional service calls to the client. Please make sure you include a test, if you add a service call. To run them use: python setup.py test