diff --git a/goal_tools/apis.py b/goal_tools/apis.py new file mode 100644 index 0000000..fa41853 --- /dev/null +++ b/goal_tools/apis.py @@ -0,0 +1,48 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import logging + +import requests + +LOG = logging.getLogger(__name__) + + +def requester(url, params={}, headers={}): + """A requests wrapper to consistently retry HTTPS queries""" + + # Try up to 3 times + retry = requests.Session() + retry.mount("https://", requests.adapters.HTTPAdapter(max_retries=3)) + return retry.get(url=url, params=params, headers=headers) + + +def decode_json(raw): + """Trap JSON decoding failures and provide more detailed errors""" + + # Gerrit's REST API prepends a JSON-breaker to avoid XSS vulnerabilities + if raw.text.startswith(")]}'"): + trimmed = raw.text[4:] + else: + trimmed = raw.text + + # Try to decode and bail with much detail if it fails + try: + decoded = json.loads(trimmed) + except Exception: + LOG.error( + '\nrequest returned %s error to query:\n\n %s\n' + '\nwith detail:\n\n %s\n', + raw, raw.url, trimmed) + raise + return decoded diff --git a/goal_tools/gerrit.py b/goal_tools/gerrit.py index 9137e11..14b6e57 100644 --- a/goal_tools/gerrit.py +++ b/goal_tools/gerrit.py @@ -10,12 +10,19 @@ # License for the specific language governing permissions and limitations # under the License. +import collections +import datetime import fileinput import logging import urllib.parse +from goal_tools import apis + LOG = logging.getLogger(__name__) +# The base URL to Gerrit REST API +GERRIT_API_URL = 'https://review.openstack.org/' + def parse_review_lists(filenames): """Generator that produces review IDs as strings. @@ -42,3 +49,57 @@ def parse_review_lists(filenames): else: # https://review.openstack.org/555353/ yield parsed.path.lstrip('/').partition('/')[0] + + +def query_gerrit(method, params={}): + """Query the Gerrit REST API""" + url = GERRIT_API_URL + method + LOG.debug('fetching %s', url) + raw = apis.requester( + url, params=params, + headers={'Accept': 'application/json'}) + return apis.decode_json(raw) + + +def _to_datetime(s): + # Ignore the trailing decimal seconds. + s = s.rpartition('.')[0] + return datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S') + + +Participant = collections.namedtuple( + 'Participant', ['role', 'name', 'email', 'date']) + + +class Review: + + def __init__(self, id, data): + self._id = id + self._data = data + + @property + def url(self): + return GERRIT_API_URL + self._id + '/' + + @property + def created(self): + return _to_datetime(self._data['created']) + + @property + def participants(self): + yield self.owner + + @property + def owner(self): + owner = self._data['owner'] + return Participant( + 'owner', + owner['name'], + owner['email'], + self.created, + ) + + +def fetch_review(review_id): + data = query_gerrit('changes/' + review_id + '/detail') + return Review(review_id, data) diff --git a/goal_tools/tests/data/55535.json b/goal_tools/tests/data/55535.json new file mode 100644 index 0000000..b94cb18 --- /dev/null +++ b/goal_tools/tests/data/55535.json @@ -0,0 +1,550 @@ +{ + "id": "openstack%2Fblazar-dashboard~master~Icbecf09ff90d7b5e563df27fe1d8db2438ac6c4a", + "project": "openstack/blazar-dashboard", + "branch": "master", + "topic": "requirements-stop-syncing", + "hashtags": [], + "change_id": "Icbecf09ff90d7b5e563df27fe1d8db2438ac6c4a", + "subject": "add lower-constraints job", + "status": "NEW", + "created": "2018-03-22 16:05:45.000000000", + "updated": "2018-04-26 06:32:01.000000000", + "submit_type": "MERGE_IF_NECESSARY", + "mergeable": true, + "submittable": false, + "insertions": 88, + "deletions": 1, + "_number": 555353, + "owner": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "labels": { + "Verified": { + "recommended": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "all": [ + { + "_account_id": 9414, + "name": "zhongshengping", + "email": "chdzsp@163.com", + "username": "ZhongShengping" + }, + { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "value": 1, + "date": "2018-04-26 03:06:40.000000000", + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + { + "_account_id": 26297, + "name": "kaka", + "email": "huang.zhiping@99cloud.net", + "username": "huang.zhiping" + }, + { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + } + ], + "values": { + "-2": "Fails", + "-1": "Doesn\u0027t seem to work", + " 0": "No score", + "+1": "Works for me", + "+2": "Verified" + }, + "value": 1, + "default_value": 0 + }, + "Code-Review": { + "approved": { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + "all": [ + { + "value": 1, + "date": "2018-04-23 03:17:42.000000000", + "_account_id": 9414, + "name": "zhongshengping", + "email": "chdzsp@163.com", + "username": "ZhongShengping" + }, + { + "value": 0, + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "value": 0, + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "value": 2, + "date": "2018-04-24 10:18:51.000000000", + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + { + "value": 0, + "_account_id": 26297, + "name": "kaka", + "email": "huang.zhiping@99cloud.net", + "username": "huang.zhiping" + }, + { + "value": 1, + "date": "2018-04-22 02:44:33.000000000", + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + { + "value": 2, + "date": "2018-04-24 01:32:26.000000000", + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + } + ], + "values": { + "-2": "Do not merge", + "-1": "This patch needs further work before it can be merged", + " 0": "No score", + "+1": "Looks good to me, but someone else must approve", + "+2": "Looks good to me (core reviewer)" + }, + "default_value": 0 + }, + "Workflow": { + "approved": { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + "all": [ + { + "_account_id": 9414, + "name": "zhongshengping", + "email": "chdzsp@163.com", + "username": "ZhongShengping" + }, + { + "value": 0, + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "value": 1, + "date": "2018-04-26 06:32:01.000000000", + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + { + "_account_id": 26297, + "name": "kaka", + "email": "huang.zhiping@99cloud.net", + "username": "huang.zhiping" + }, + { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + { + "value": 0, + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + } + ], + "values": { + "-1": "Work in progress", + " 0": "Ready for reviews", + "+1": "Approved" + }, + "default_value": 0 + } + }, + "permitted_labels": {}, + "removable_reviewers": [], + "reviewers": { + "CC": [ + { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + } + ], + "REVIEWER": [ + { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + { + "_account_id": 9414, + "name": "zhongshengping", + "email": "chdzsp@163.com", + "username": "ZhongShengping" + }, + { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + { + "_account_id": 26297, + "name": "kaka", + "email": "huang.zhiping@99cloud.net", + "username": "huang.zhiping" + } + ] + }, + "reviewer_updates": [], + "messages": [ + { + "id": "df7087c5_9e5659ce", + "author": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "date": "2018-03-22 16:05:45.000000000", + "message": "Uploaded patch set 1.", + "_revision_number": 1 + }, + { + "id": "df7087c5_b2efd11b", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-03-22 16:31:55.000000000", + "message": "Patch Set 1: Verified-1\n\nBuild failed (check pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/1/check/openstack-tox-pep8/341dced/ : SUCCESS in 5m 12s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/1/check/openstack-tox-py27/51d2274/ : SUCCESS in 5m 04s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/1/check/openstack-tox-py35/8749020/ : SUCCESS in 5m 05s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/1/check/build-openstack-sphinx-docs/c5f3add/html/ : SUCCESS in 4m 00s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/1/check/openstack-tox-lower-constraints/5f9282b/ : FAILURE in 5m 13s", + "_revision_number": 1 + }, + { + "id": "df7087c5_325181da", + "author": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "date": "2018-03-22 16:33:45.000000000", + "message": "Patch Set 1:\n\nIt looks like this repo is going to need a special job variant that installs horizon.", + "_revision_number": 1 + }, + { + "id": "bf659307_4452f63c", + "author": { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + "date": "2018-04-02 08:31:55.000000000", + "message": "Patch Set 1:\n\nHow do other dashboard repositories handle the situation where horizon is required? Only this repo needs a special job?", + "_revision_number": 1 + }, + { + "id": "bf659307_03e0d1f9", + "author": { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + "date": "2018-04-10 06:07:47.000000000", + "message": "Patch Set 2: Patch Set 1 was rebased", + "_revision_number": 2 + }, + { + "id": "bf659307_43656944", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-10 06:24:11.000000000", + "message": "Patch Set 2: Verified-1\n\nBuild failed (check pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/2/check/openstack-tox-pep8/c4b9713/ : SUCCESS in 5m 18s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/2/check/openstack-tox-py27/e821d02/ : SUCCESS in 5m 22s\n- requirements-check http://logs.openstack.org/53/555353/2/check/requirements-check/b7a4041/ : FAILURE in 3m 18s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/2/check/openstack-tox-py35/f1a84ef/ : SUCCESS in 4m 59s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/2/check/build-openstack-sphinx-docs/2bd9664/html/ : SUCCESS in 6m 22s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/2/check/openstack-tox-lower-constraints/95d4f67/ : SUCCESS in 4m 11s", + "_revision_number": 2 + }, + { + "id": "bf659307_b1d7e44f", + "author": { + "_account_id": 26297, + "name": "kaka", + "email": "huang.zhiping@99cloud.net", + "username": "huang.zhiping" + }, + "date": "2018-04-10 12:24:36.000000000", + "message": "Patch Set 2: Code-Review+1", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_851fd7b5", + "author": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "date": "2018-04-20 20:29:17.000000000", + "message": "Uploaded patch set 3.", + "_revision_number": 3 + }, + { + "id": "9f6a8fd7_b8fa3eba", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-20 20:55:30.000000000", + "message": "Patch Set 3: Verified-1\n\nBuild failed (check pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/3/check/openstack-tox-pep8/262ed55/ : SUCCESS in 6m 06s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/3/check/openstack-tox-py27/c69f570/ : SUCCESS in 5m 11s\n- requirements-check http://logs.openstack.org/53/555353/3/check/requirements-check/24e8e67/ : SUCCESS in 3m 02s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/3/check/openstack-tox-py35/78cc5d4/ : SUCCESS in 6m 20s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/3/check/build-openstack-sphinx-docs/4eac6f4/html/ : SUCCESS in 4m 33s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/3/check/openstack-tox-lower-constraints/eb48f89/ : FAILURE in 3m 21s", + "_revision_number": 3 + }, + { + "id": "9f6a8fd7_cc2f2f86", + "author": { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + "date": "2018-04-22 02:25:01.000000000", + "message": "Patch Set 3:\n\nl-c job fails as there is no blazarcient 1.0.0 on PyPI.", + "_revision_number": 3 + }, + { + "id": "9f6a8fd7_4c59df32", + "author": { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + "date": "2018-04-22 02:28:56.000000000", + "message": "Uploaded patch set 4.", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_6c7703a2", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-22 02:39:16.000000000", + "message": "Patch Set 4: Verified+1\n\nBuild succeeded (check pipeline).\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/4/check/openstack-tox-pep8/a417873/ : SUCCESS in 4m 32s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/4/check/openstack-tox-py27/7e6f704/ : SUCCESS in 5m 57s\n- requirements-check http://logs.openstack.org/53/555353/4/check/requirements-check/b1ed799/ : SUCCESS in 2m 46s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/4/check/openstack-tox-py35/2b52b85/ : SUCCESS in 5m 02s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/4/check/build-openstack-sphinx-docs/f8955d6/html/ : SUCCESS in 3m 23s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/4/check/openstack-tox-lower-constraints/5887317/ : SUCCESS in 4m 47s", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_6c9ce3bf", + "author": { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + "date": "2018-04-22 02:44:33.000000000", + "message": "Patch Set 4: Code-Review+1\n\nI am fine with this from my knowledge on this.", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_0caf627d", + "author": { + "_account_id": 9414, + "name": "zhongshengping", + "email": "chdzsp@163.com", + "username": "ZhongShengping" + }, + "date": "2018-04-23 03:17:42.000000000", + "message": "Patch Set 4: Code-Review+1", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_9576fc6f", + "author": { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + "date": "2018-04-24 01:32:26.000000000", + "message": "Patch Set 4: Code-Review+2", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_6e9112f1", + "author": { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + "date": "2018-04-24 10:18:51.000000000", + "message": "Patch Set 4: Workflow+1 Code-Review+2", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_8e8ca6d7", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-24 10:19:23.000000000", + "message": "Patch Set 4: -Verified\n\nStarting gate jobs.", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_31029b0b", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-24 10:30:45.000000000", + "message": "Patch Set 4: Verified-2\n\nBuild failed (gate pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/4/gate/openstack-tox-pep8/c8de9ef/ : SUCCESS in 5m 04s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/4/gate/openstack-tox-py27/2b6f268/ : FAILURE in 5m 09s\n- requirements-check http://logs.openstack.org/53/555353/4/gate/requirements-check/8ff01b9/ : SUCCESS in 4m 51s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/4/gate/openstack-tox-py35/3879fd8/ : FAILURE in 5m 56s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/4/gate/build-openstack-sphinx-docs/fd2b88c/html/ : SUCCESS in 4m 09s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/4/gate/openstack-tox-lower-constraints/121deb2/ : FAILURE in 5m 36s", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_91acb09f", + "author": { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + "date": "2018-04-25 07:58:51.000000000", + "message": "Patch Set 4:\n\nrecheck", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_51f7f88b", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-25 08:06:19.000000000", + "message": "Patch Set 4: Verified-1\n\nBuild failed (check pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/4/check/openstack-tox-pep8/4153204/ : SUCCESS in 5m 55s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/4/check/openstack-tox-py27/3e7f01f/ : FAILURE in 5m 01s\n- requirements-check http://logs.openstack.org/53/555353/4/check/requirements-check/cd10778/ : SUCCESS in 3m 43s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/4/check/openstack-tox-py35/ce4b440/ : FAILURE in 5m 10s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/4/check/build-openstack-sphinx-docs/8fee641/html/ : SUCCESS in 5m 15s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/4/check/openstack-tox-lower-constraints/9609359/ : FAILURE in 5m 47s", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_bf973bec", + "author": { + "_account_id": 841, + "name": "Akihiro Motoki", + "email": "amotoki@gmail.com", + "username": "amotoki" + }, + "date": "2018-04-25 09:35:55.000000000", + "message": "Patch Set 4:\n\nunit test failures is due to the horizon planned change. As announced before [0], horizon test helper is no longer set up by default after Rocky-1 [1]. Dashboard tests which still depends on mox must declare use_mox\u003dTrue.\n\n[0] http://lists.openstack.org/pipermail/openstack-dev/2018-March/128486.html\n[1] http://lists.openstack.org/pipermail/openstack-dev/2018-April/129648.html", + "_revision_number": 4 + }, + { + "id": "9f6a8fd7_3c5b22f6", + "author": { + "_account_id": 23840, + "name": "Hiroaki Kobayashi", + "email": "kobayashi.hiroaki@lab.ntt.co.jp", + "username": "hiro-kobayashi" + }, + "date": "2018-04-26 02:59:34.000000000", + "message": "Patch Set 5: Patch Set 4 was rebased", + "_revision_number": 5 + }, + { + "id": "9f6a8fd7_5cd5b61a", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-26 03:06:40.000000000", + "message": "Patch Set 5: Verified+1\n\nBuild succeeded (check pipeline).\n\n- openstack-tox-pep8 http://logs.openstack.org/53/555353/5/check/openstack-tox-pep8/18c1cc5/ : SUCCESS in 5m 26s\n- openstack-tox-py27 http://logs.openstack.org/53/555353/5/check/openstack-tox-py27/ea97804/ : SUCCESS in 4m 56s\n- requirements-check http://logs.openstack.org/53/555353/5/check/requirements-check/04dff5c/ : SUCCESS in 3m 05s\n- openstack-tox-py35 http://logs.openstack.org/53/555353/5/check/openstack-tox-py35/e961aef/ : SUCCESS in 5m 08s\n- build-openstack-sphinx-docs http://logs.openstack.org/53/555353/5/check/build-openstack-sphinx-docs/fa408bc/html/ : SUCCESS in 4m 04s\n- openstack-tox-lower-constraints http://logs.openstack.org/53/555353/5/check/openstack-tox-lower-constraints/16d68c9/ : SUCCESS in 5m 18s", + "_revision_number": 5 + }, + { + "id": "9f6a8fd7_bab17246", + "author": { + "_account_id": 8878, + "name": "Masahito Muroi", + "email": "muroi.masahito@lab.ntt.co.jp", + "username": "masa" + }, + "date": "2018-04-26 06:32:01.000000000", + "message": "Patch Set 5: Workflow+1", + "_revision_number": 5 + } + ] +} diff --git a/goal_tools/tests/data/561507.json b/goal_tools/tests/data/561507.json new file mode 100644 index 0000000..658a68a --- /dev/null +++ b/goal_tools/tests/data/561507.json @@ -0,0 +1,320 @@ +{ + "id": "openstack%2Freleases~master~I08015ec6c5e0a0f3cd552c2b5a077c0fb86df148", + "project": "openstack/releases", + "branch": "master", + "hashtags": [], + "change_id": "I08015ec6c5e0a0f3cd552c2b5a077c0fb86df148", + "subject": "Change storlets release model to cycle-with-milestones", + "status": "MERGED", + "created": "2018-04-16 04:50:17.000000000", + "updated": "2018-04-19 12:57:36.000000000", + "submitted": "2018-04-19 12:57:36.000000000", + "submittable": false, + "insertions": 6, + "deletions": 1, + "_number": 561507, + "owner": { + "_account_id": 4608, + "name": "Kota Tsuyuzaki", + "email": "tsuyuzaki.kota@lab.ntt.co.jp", + "username": "tsuyuzaki-kota" + }, + "labels": { + "Verified": { + "approved": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "all": [ + { + "value": 0, + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "value": 0, + "_account_id": 9816, + "name": "Takashi Kajinami", + "email": "kajinamit@nttdata.co.jp", + "username": "kajinamit" + }, + { + "value": 2, + "date": "2018-04-19 12:57:35.000000000", + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "value": 0, + "_account_id": 308, + "name": "Thierry Carrez", + "email": "thierry@openstack.org", + "username": "ttx" + } + ], + "values": { + "-2": "Fails", + "-1": "Doesn\u0027t seem to work", + " 0": "No score", + "+1": "Works for me", + "+2": "Verified" + }, + "default_value": 0 + }, + "Code-Review": { + "approved": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "all": [ + { + "value": 2, + "date": "2018-04-16 14:45:28.000000000", + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "value": 1, + "date": "2018-04-16 07:13:17.000000000", + "_account_id": 9816, + "name": "Takashi Kajinami", + "email": "kajinamit@nttdata.co.jp", + "username": "kajinamit" + }, + { + "value": 0, + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "value": 2, + "date": "2018-04-18 10:27:59.000000000", + "_account_id": 308, + "name": "Thierry Carrez", + "email": "thierry@openstack.org", + "username": "ttx" + } + ], + "values": { + "-2": "Do not merge", + "-1": "This patch needs further work before it can be merged", + " 0": "No score", + "+1": "Looks good to me, but someone else must approve", + "+2": "Looks good to me (core reviewer)" + }, + "default_value": 0 + }, + "Workflow": { + "approved": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "all": [ + { + "value": 1, + "date": "2018-04-19 12:49:44.000000000", + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "value": 0, + "_account_id": 9816, + "name": "Takashi Kajinami", + "email": "kajinamit@nttdata.co.jp", + "username": "kajinamit" + }, + { + "value": 0, + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + { + "value": 0, + "_account_id": 308, + "name": "Thierry Carrez", + "email": "thierry@openstack.org", + "username": "ttx" + } + ], + "values": { + "-1": "Work in progress", + " 0": "Ready for reviews", + "+1": "Approved" + }, + "default_value": 0 + } + }, + "permitted_labels": {}, + "removable_reviewers": [], + "reviewers": { + "REVIEWER": [ + { + "_account_id": 308, + "name": "Thierry Carrez", + "email": "thierry@openstack.org", + "username": "ttx" + }, + { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + { + "_account_id": 9816, + "name": "Takashi Kajinami", + "email": "kajinamit@nttdata.co.jp", + "username": "kajinamit" + }, + { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + } + ] + }, + "reviewer_updates": [], + "messages": [ + { + "id": "9f6a8fd7_80b757d9", + "author": { + "_account_id": 4608, + "name": "Kota Tsuyuzaki", + "email": "tsuyuzaki.kota@lab.ntt.co.jp", + "username": "tsuyuzaki-kota" + }, + "date": "2018-04-16 04:50:17.000000000", + "message": "Uploaded patch set 1.", + "_revision_number": 1 + }, + { + "id": "9f6a8fd7_8005177d", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-16 04:57:28.000000000", + "message": "Patch Set 1: Verified-1\n\nBuild failed (check pipeline). For information on how to proceed, see\nhttp://docs.openstack.org/infra/manual/developers.html#automated-testing\n\n\n- openstack-tox-validate http://logs.openstack.org/07/561507/1/check/openstack-tox-validate/5722934/ : FAILURE in 3m 01s\n- releases-tox-list-changes http://logs.openstack.org/07/561507/1/check/releases-tox-list-changes/61376fd/ : SUCCESS in 2m 30s\n- build-openstack-sphinx-docs http://logs.openstack.org/07/561507/1/check/build-openstack-sphinx-docs/db4fb75/html/ : SUCCESS in 6m 19s", + "_revision_number": 1 + }, + { + "id": "9f6a8fd7_e0ef33ae", + "author": { + "_account_id": 4608, + "name": "Kota Tsuyuzaki", + "email": "tsuyuzaki.kota@lab.ntt.co.jp", + "username": "tsuyuzaki-kota" + }, + "date": "2018-04-16 05:00:21.000000000", + "message": "Uploaded patch set 2.", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_400dbf8e", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-16 05:06:06.000000000", + "message": "Patch Set 2: Verified+1\n\nBuild succeeded (check pipeline).\n\n- openstack-tox-validate http://logs.openstack.org/07/561507/2/check/openstack-tox-validate/95549e4/ : SUCCESS in 3m 09s\n- releases-tox-list-changes http://logs.openstack.org/07/561507/2/check/releases-tox-list-changes/09f1182/ : SUCCESS in 3m 33s\n- build-openstack-sphinx-docs http://logs.openstack.org/07/561507/2/check/build-openstack-sphinx-docs/2e6c15e/html/ : SUCCESS in 5m 16s", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_9688fd71", + "author": { + "_account_id": 9816, + "name": "Takashi Kajinami", + "email": "kajinamit@nttdata.co.jp", + "username": "kajinamit" + }, + "date": "2018-04-16 07:13:17.000000000", + "message": "Patch Set 2: Code-Review+1\n\nLGTM.\n\nCurrently, Swift, on which storlets depend on, follows cycle-with-intermediary.\nBut I know that they also have releases based on milestone, so I think that having milestone based release at least can work for us.", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_3824730d", + "author": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "date": "2018-04-16 14:45:28.000000000", + "message": "Patch Set 2: Code-Review+2\n\nThis looks good. The deadline for the first milestone is in a few days, so I will leave this open in case you want to update the SHA to refer to a newer commit.", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_9741970e", + "author": { + "_account_id": 308, + "name": "Thierry Carrez", + "email": "thierry@openstack.org", + "username": "ttx" + }, + "date": "2018-04-18 10:27:59.000000000", + "message": "Patch Set 2: Code-Review+2\n\nLooking good, leaving it open until Thursday for last-minute SHA updates.", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_5318d3d1", + "author": { + "_account_id": 2472, + "name": "Doug Hellmann", + "email": "doug@doughellmann.com", + "username": "doug-hellmann" + }, + "date": "2018-04-19 12:49:44.000000000", + "message": "Patch Set 2: Workflow+1", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_f30ec78b", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-19 12:49:56.000000000", + "message": "Patch Set 2: -Verified\n\nStarting gate jobs.", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_93550b20", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-19 12:57:35.000000000", + "message": "Patch Set 2: Verified+2\n\nBuild succeeded (gate pipeline).\n\n- openstack-tox-validate http://logs.openstack.org/07/561507/2/gate/openstack-tox-validate/c2390b6/ : SUCCESS in 4m 49s\n- build-openstack-sphinx-docs http://logs.openstack.org/07/561507/2/gate/build-openstack-sphinx-docs/f632774/html/ : SUCCESS in 6m 13s", + "_revision_number": 2 + }, + { + "id": "9f6a8fd7_3332ff11", + "author": { + "_account_id": 22348, + "name": "Zuul", + "username": "zuul" + }, + "date": "2018-04-19 12:57:36.000000000", + "message": "Change has been successfully merged by Zuul", + "_revision_number": 2 + } + ] +} diff --git a/goal_tools/tests/test_gerrit.py b/goal_tools/tests/test_gerrit.py index 28f68f2..74354b8 100644 --- a/goal_tools/tests/test_gerrit.py +++ b/goal_tools/tests/test_gerrit.py @@ -10,7 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime +import json import os.path +import pkgutil import textwrap from goal_tools import gerrit @@ -63,3 +66,32 @@ class TestParseReviewLists(base.TestCase): expected = ['561507', '555353'] actual = list(gerrit.parse_review_lists([self.ids_name])) self.assertEqual(expected, actual) + + +class TestReview(base.TestCase): + + _data = json.loads( + pkgutil.get_data('goal_tools.tests', 'data/55535.json').decode('utf-8') + ) + + def setUp(self): + super().setUp() + self.rev = gerrit.Review('55535', self._data) + + def test_url(self): + self.assertEqual('https://review.openstack.org/55535/', self.rev.url) + + def test_created(self): + self.assertEqual( + datetime.datetime(2018, 3, 22, 16, 5, 45), + self.rev.created, + ) + + def test_owner(self): + owner = self.rev.owner + self.assertEqual('Doug Hellmann', owner.name) + self.assertEqual('doug@doughellmann.com', owner.email) + self.assertEqual( + datetime.datetime(2018, 3, 22, 16, 5, 45), + owner.date, + )