diff --git a/gerrit_client.py b/gerrit_client.py index 9e9082d25..0228d5ac0 100755 --- a/gerrit_client.py +++ b/gerrit_client.py @@ -153,6 +153,22 @@ def CMDchanges(parser, args): write_result(result, opt) +@subcommand.usage('[args ...]') +def CMDrelatedchanges(parser, args): + parser.add_option('-c', '--change', type=str, help='change id') + parser.add_option('-r', '--revision', type=str, help='revision id') + + (opt, args) = parser.parse_args(args) + + result = gerrit_util.GetRelatedChanges( + urlparse.urlparse(opt.host).netloc, + change=opt.change, + revision=opt.revision, + ) + logging.info(result) + write_result(result, opt) + + @subcommand.usage('[args ...]') def CMDcreatechange(parser, args): parser.add_option('-s', '--subject', help='subject for change') diff --git a/gerrit_util.py b/gerrit_util.py index 81d0f88d1..1f44a799e 100644 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -703,6 +703,12 @@ def GetChangeRobotComments(host, change): return ReadHttpJsonResponse(CreateHttpConn(host, path)) +def GetRelatedChanges(host, change, revision='current'): + """Gets the related changes for a given change and revision.""" + path = 'changes/%s/revisions/%s/related' % (change, revision) + return ReadHttpJsonResponse(CreateHttpConn(host, path)) + + def AbandonChange(host, change, msg=''): """Abandons a Gerrit change.""" path = 'changes/%s/abandon' % change diff --git a/recipes/README.recipes.md b/recipes/README.recipes.md index 0ee202d58..141040eaf 100644 --- a/recipes/README.recipes.md +++ b/recipes/README.recipes.md @@ -361,7 +361,7 @@ Module for interact with Gerrit endpoints Wrapper for easy calling of gerrit_utils steps. -— **def [abandon\_change](/recipes/recipe_modules/gerrit/api.py#173)(self, host, change, message=None, name=None, step_test_data=None):** +— **def [abandon\_change](/recipes/recipe_modules/gerrit/api.py#209)(self, host, change, message=None, name=None, step_test_data=None):** — **def [create\_gerrit\_branch](/recipes/recipe_modules/gerrit/api.py#32)(self, host, project, branch, commit, \*\*kwargs):** @@ -408,6 +408,25 @@ Gets a branch from given project and commit Returns: The revision of the branch +— **def [get\_related\_changes](/recipes/recipe_modules/gerrit/api.py#173)(self, host, change, revision='current', step_test_data=None):** + +Queries related changes for a given host, change, and revision. + +Args: + * host: URL of Gerrit host to query. + * change: The change-id of the change to get related changes for as + documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-id + * revision: The revision-id of the revision to get related changes for as + documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#revision-id + This defaults to current, which names the most recent patch set. + * step_test_data: Optional mock test data for the underlying gerrit client. + +Returns: + A related changes dictionary as documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#related-changes-info + — **def [get\_revision\_info](/recipes/recipe_modules/gerrit/api.py#90)(self, host, change, patchset, timeout=None, step_test_data=None):** Returns the info for a given patchset of a given change. @@ -421,7 +440,7 @@ Returns: A dict for the target revision as documented here: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes -— **def [move\_changes](/recipes/recipe_modules/gerrit/api.py#193)(self, host, project, from_branch, to_branch, step_test_data=None):** +— **def [move\_changes](/recipes/recipe_modules/gerrit/api.py#229)(self, host, project, from_branch, to_branch, step_test_data=None):** ### *recipe_modules* / [git](/recipes/recipe_modules/git) [DEPS](/recipes/recipe_modules/git/__init__.py#1): [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step] diff --git a/recipes/recipe_modules/gerrit/api.py b/recipes/recipe_modules/gerrit/api.py index 0b6d3120a..4bb028d6e 100644 --- a/recipes/recipe_modules/gerrit/api.py +++ b/recipes/recipe_modules/gerrit/api.py @@ -170,6 +170,42 @@ class GerritApi(recipe_api.RecipeApi): **kwargs ).json.output + def get_related_changes(self, host, change, revision='current', step_test_data=None): + """Queries related changes for a given host, change, and revision. + + Args: + * host: URL of Gerrit host to query. + * change: The change-id of the change to get related changes for as + documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-id + * revision: The revision-id of the revision to get related changes for as + documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#revision-id + This defaults to current, which names the most recent patch set. + * step_test_data: Optional mock test data for the underlying gerrit client. + + Returns: + A related changes dictionary as documented here: + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#related-changes-info + + """ + args = [ + 'relatedchanges', + '--host', + host, + '--change', + change, + '--revision', + revision, + '--json_file', + self.m.json.output(), + ] + if not step_test_data: + step_test_data = lambda: self.test_api.get_related_changes_response_data() + + return self('relatedchanges', args, + step_test_data=step_test_data).json.output + def abandon_change(self, host, change, message=None, name=None, step_test_data=None): args = [ diff --git a/recipes/recipe_modules/gerrit/examples/full.expected/basic.json b/recipes/recipe_modules/gerrit/examples/full.expected/basic.json index 15e509104..0a4be4c31 100644 --- a/recipes/recipe_modules/gerrit/examples/full.expected/basic.json +++ b/recipes/recipe_modules/gerrit/examples/full.expected/basic.json @@ -156,6 +156,57 @@ "@@@STEP_LOG_END@json.output@@@" ] }, + { + "cmd": [ + "vpython", + "-u", + "RECIPE_REPO[depot_tools]/gerrit_client.py", + "relatedchanges", + "--host", + "https://chromium-review.googlesource.com", + "--change", + "58478", + "--revision", + "2", + "--json_file", + "/path/to/tmp/json" + ], + "env": { + "PATH": ":RECIPE_REPO[depot_tools]" + }, + "infra_step": true, + "name": "gerrit relatedchanges", + "~followup_annotations": [ + "@@@STEP_LOG_LINE@json.output@{@@@", + "@@@STEP_LOG_LINE@json.output@ \"changes\": [@@@", + "@@@STEP_LOG_LINE@json.output@ {@@@", + "@@@STEP_LOG_LINE@json.output@ \"_change_number\": 58478, @@@", + "@@@STEP_LOG_LINE@json.output@ \"_current_revision_number\": 2, @@@", + "@@@STEP_LOG_LINE@json.output@ \"_revision_number\": 2, @@@", + "@@@STEP_LOG_LINE@json.output@ \"change_id\": \"Ic62ae3103fca2214904dbf2faf4c861b5f0ae9b5\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"commit\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"author\": {@@@", + "@@@STEP_LOG_LINE@json.output@ \"date\": \"2014-07-12 15:04:24.000000000\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"email\": \"example@example.com\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"name\": \"Example Name\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"tz\": 120@@@", + "@@@STEP_LOG_LINE@json.output@ }, @@@", + "@@@STEP_LOG_LINE@json.output@ \"commit\": \"78847477532e386f5a2185a4e8c90b2509e354e3\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"parents\": [@@@", + "@@@STEP_LOG_LINE@json.output@ {@@@", + "@@@STEP_LOG_LINE@json.output@ \"commit\": \"bb499510bbcdbc9164d96b0dbabb4aa45f59a87e\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ ], @@@", + "@@@STEP_LOG_LINE@json.output@ \"subject\": \"Remove Solr\"@@@", + "@@@STEP_LOG_LINE@json.output@ }, @@@", + "@@@STEP_LOG_LINE@json.output@ \"project\": \"gerrit\", @@@", + "@@@STEP_LOG_LINE@json.output@ \"status\": \"NEW\"@@@", + "@@@STEP_LOG_LINE@json.output@ }@@@", + "@@@STEP_LOG_LINE@json.output@ ]@@@", + "@@@STEP_LOG_LINE@json.output@}@@@", + "@@@STEP_LOG_END@json.output@@@" + ] + }, { "cmd": [ "vpython", diff --git a/recipes/recipe_modules/gerrit/examples/full.py b/recipes/recipe_modules/gerrit/examples/full.py index dfd4e6b67..1a281771b 100644 --- a/recipes/recipe_modules/gerrit/examples/full.py +++ b/recipes/recipe_modules/gerrit/examples/full.py @@ -34,6 +34,10 @@ def RunSteps(api): start=1, limit=1, ) + related_changes = api.gerrit.get_related_changes(host, + change='58478', + revision='2') + assert len(related_changes["changes"]) == 1 # Query which returns no changes is still successful query. empty_list = api.gerrit.get_changes( @@ -69,5 +73,7 @@ def GenTests(api): api.gerrit.make_gerrit_get_branch_response_data()) + api.step_data('gerrit move changes', api.gerrit.get_move_change_response_data(branch='main')) + + api.step_data('gerrit relatedchanges', + api.gerrit.get_related_changes_response_data()) + api.step_data('gerrit changes empty query', api.gerrit.get_empty_changes_response_data())) diff --git a/recipes/recipe_modules/gerrit/test_api.py b/recipes/recipe_modules/gerrit/test_api.py index e7e031de9..27e73ba31 100644 --- a/recipes/recipe_modules/gerrit/test_api.py +++ b/recipes/recipe_modules/gerrit/test_api.py @@ -32,6 +32,34 @@ class GerritTestApi(recipe_test_api.RecipeTestApi): data.update(kwargs) return data + @staticmethod + def _related_changes_data(**kwargs): + data = { + "changes": [{ + "project": "gerrit", + "change_id": "Ic62ae3103fca2214904dbf2faf4c861b5f0ae9b5", + "commit": { + "commit": "78847477532e386f5a2185a4e8c90b2509e354e3", + "parents": [{ + "commit": "bb499510bbcdbc9164d96b0dbabb4aa45f59a87e" + }], + "author": { + "name": "Example Name", + "email": "example@example.com", + "date": "2014-07-12 15:04:24.000000000", + "tz": 120 + }, + "subject": "Remove Solr" + }, + "_change_number": 58478, + "_revision_number": 2, + "_current_revision_number": 2, + "status": "NEW" + }] + } + data.update(kwargs) + return data + def _make_gerrit_response_json(self, data): return self.m.json.output(data) @@ -56,3 +84,6 @@ class GerritTestApi(recipe_test_api.RecipeTestApi): def get_move_change_response_data(self, **kwargs): return self._make_gerrit_response_json([self._gerrit_change_data(**kwargs)]) + + def get_related_changes_response_data(self, **kwargs): + return self._make_gerrit_response_json(self._related_changes_data(**kwargs)) diff --git a/tests/gerrit_client_test.py b/tests/gerrit_client_test.py index d1a94440c..1520b9ea2 100755 --- a/tests/gerrit_client_test.py +++ b/tests/gerrit_client_test.py @@ -56,6 +56,16 @@ class TestGerritClient(unittest.TestCase): start=20, o_params=['op1', 'op2']) + @mock.patch('gerrit_util.GetRelatedChanges', return_value='') + def test_relatedchanges(self, util_mock): + gerrit_client.main([ + 'relatedchanges', '--host', 'https://example.org/foo', '--change', + 'foo-change-id', '--revision', 'foo-revision-id' + ]) + util_mock.assert_called_once_with('example.org', + change='foo-change-id', + revision='foo-revision-id') + @mock.patch('gerrit_util.CreateChange', return_value={}) def test_createchange(self, util_mock): gerrit_client.main([