util: Add reviewers using account ID's when possible
[gem5.git] / util / gerrit-bot / gerrit.py
1 # Copyright (c) 2020 The Regents of the University of California
2 # All Rights Reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 import copy
28 import json
29 import requests
30 from types import SimpleNamespace
31 from urllib.parse import urljoin
32
33 class GerritResponseParser:
34 @staticmethod
35 def get_json_content(response):
36 assert(isinstance(response, requests.Response))
37
38 # If the status code is not in the 200s range, it doesn't have content.
39 if response.status_code >= 300:
40 return None
41
42 # Transform response.content to a Python3 string.
43 # response.content is a byte array containing the response.
44 # The first 4 bytes are b")]}\", which doesn't belong to JSON content.
45 # The byte array is encoded by utf-8.
46 content = response.content[4:].decode("utf-8")
47 json_content = json.loads(content)
48 return json_content
49
50 # TODO: parsing method for each Gerrit data structure
51 @staticmethod
52 def parse(response):
53 json_content = GerritResponseParser.get_json_content(response)
54 if not json_content:
55 return None
56 return SimpleNamespace(**json_content)
57
58
59 class GerritRestAPI:
60 def __init__(self, auth, api_entry_point, timeout):
61 self.username = auth[0]
62 self.password = auth[1]
63 self.api_entry_point = api_entry_point
64 self.timeout = timeout
65
66 # helper methods for sending GET and POST requests
67 def _get(self, endpoint, params = None):
68 request_url = urljoin(self.api_entry_point, endpoint)
69 return requests.get(request_url,
70 params = params,
71 timeout = self.timeout,
72 auth = (self.username, self.password))
73 def _post(self, endpoint, json_content):
74 request_url = urljoin(self.api_entry_point, endpoint)
75 return requests.post(request_url,
76 json = json_content,
77 timeout = self.timeout,
78 auth = (self.username, self.password))
79
80 # --------------- Account Endpoints ---------------
81 # https://gerrit-review.googlesource.com/Documentation/
82 # rest-api-accounts.html#get-account
83 def get_account(self, account_id="self"):
84 """ get an account detail from an account_id """
85 return self._get(f"/accounts/{account_id}")
86
87 # https://gerrit-review.googlesource.com/Documentation/
88 # rest-api-accounts.html#query-account
89 def query_account(self, query, limit = None):
90 """ get accounts based on the query """
91 params = { "q": query }
92 if limit:
93 params["n"] = str(limit)
94 return self._get(f"/accounts/", params)
95
96 # --------------- Changes Endpoints ---------------
97 # https://gerrit-review.googlesource.com/Documentation/
98 # rest-api-changes.html#list-changes
99 def query_changes(self, query, limit=None, optional_field=None):
100 """ query changes with maximum limit returned queries """
101 endpoint = f"/changes/"
102 params = { "q": query }
103 if limit:
104 params["n"] = str(limit)
105 if optional_field:
106 params["o"] = optional_field
107 return self._get(endpoint, params)
108
109 # --------------- Reviewer Endpoints ---------------
110 # https://gerrit-review.googlesource.com/Documentation/
111 # rest-api-changes.html#list-reviewers
112 def list_reviewers(self, change_id):
113 """ list reviewers of a change """
114 return self._get(f"/changes/{change_id}/reviewers")
115 def add_reviewer(self, change_id, reviewer_email):
116 """ add a reviewer using an email address """
117 data = {"reviewer": reviewer_email}
118 return self._post(f"/changes/{change_id}/reviewers/", data)