Add labels information in files-owners API
Added a labels section containing the labels representation
of the current patchset.
Here an example of response:
```
GET /changes/{change-id}/revisions/{revision-id}/owners~files-owners
{
"files": {
"AJavaFile.java":[
{ "name":"John", "id": 1000002 },
{ "name":"Bob", "id": 1000001 }
],
"Aptyhonfileroot.py":[
{ "name":"John", "id": 1000002 },
{ "name":"Bob", "id": 1000001 },
{ "name":"Jack", "id": 1000003 }
]
},
"owners_labels" : {
"1000002": {
"Verified": 1,
"Code-Review": 0
},
"1000001": {
"Code-Review": -2
}
}
}
```
The files information have been nested under the "files" key.
Change-Id: I9f0e509e54978994b0712b3d9b54433eb37772d8
diff --git a/owners/src/main/java/com/googlesource/gerrit/owners/entities/FilesOwnersResponse.java b/owners/src/main/java/com/googlesource/gerrit/owners/entities/FilesOwnersResponse.java
new file mode 100644
index 0000000..71648db
--- /dev/null
+++ b/owners/src/main/java/com/googlesource/gerrit/owners/entities/FilesOwnersResponse.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// 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.
+
+package com.googlesource.gerrit.owners.entities;
+
+import com.google.common.base.Objects;
+import java.util.Map;
+import java.util.Set;
+
+/* Files to Owners response API representation */
+public class FilesOwnersResponse {
+
+ private final Map<String, Set<Owner>> files;
+ private final Map<Integer, Map<String, Integer>> ownersLabels;
+
+ public FilesOwnersResponse(
+ Map<Integer, Map<String, Integer>> ownersLabels, Map<String, Set<Owner>> files) {
+ this.ownersLabels = ownersLabels;
+ this.files = files;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FilesOwnersResponse that = (FilesOwnersResponse) o;
+ return Objects.equal(files, that.files) && Objects.equal(ownersLabels, that.ownersLabels);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(files, ownersLabels);
+ }
+}
diff --git a/owners/src/main/java/com/googlesource/gerrit/owners/restapi/GetFilesOwners.java b/owners/src/main/java/com/googlesource/gerrit/owners/restapi/GetFilesOwners.java
index a59e876..b63d902 100644
--- a/owners/src/main/java/com/googlesource/gerrit/owners/restapi/GetFilesOwners.java
+++ b/owners/src/main/java/com/googlesource/gerrit/owners/restapi/GetFilesOwners.java
@@ -19,10 +19,14 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.client.ListChangesOption;
+import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.change.RevisionResource;
@@ -34,7 +38,10 @@
import com.googlesource.gerrit.owners.common.Accounts;
import com.googlesource.gerrit.owners.common.PathOwners;
import com.googlesource.gerrit.owners.common.PluginSettings;
+import com.googlesource.gerrit.owners.entities.FilesOwnersResponse;
import com.googlesource.gerrit.owners.entities.Owner;
+import java.util.EnumSet;
+import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -49,6 +56,7 @@
private final AccountCache accountCache;
private final GitRepositoryManager repositoryManager;
private final PluginSettings pluginSettings;
+ private final GerritApi gerritApi;
@Inject
GetFilesOwners(
@@ -56,12 +64,14 @@
Accounts accounts,
AccountCache accountCache,
GitRepositoryManager repositoryManager,
- PluginSettings pluginSettings) {
+ PluginSettings pluginSettings,
+ GerritApi gerritApi) {
this.patchListCache = patchListCache;
this.accounts = accounts;
this.accountCache = accountCache;
this.repositoryManager = repositoryManager;
this.pluginSettings = pluginSettings;
+ this.gerritApi = gerritApi;
}
@Override
@@ -69,6 +79,7 @@
throws AuthException, BadRequestException, ResourceConflictException, Exception {
PatchSet ps = revision.getPatchSet();
Change change = revision.getChange();
+ int id = revision.getChangeResource().getChange().getChangeId();
try (Repository repository = repositoryManager.openRepository(change.getProject())) {
PatchList patchList = patchListCache.get(change, ps);
@@ -90,10 +101,54 @@
.flatMap(Optional::stream)
.collect(Collectors.toSet()));
- return Response.ok(fileToOwners);
+ return Response.ok(new FilesOwnersResponse(getLabels(id), fileToOwners));
}
}
+ /**
+ * This method returns ta Map representing the "owners_labels" object of the response. When
+ * serialized the Map, has to to return the following JSON: the following JSON:
+ *
+ * <pre>
+ * {
+ * "1000001" : {
+ * "Code-Review" : 1,
+ * "Verified" : 0
+ * },
+ * "1000003" : {
+ * "Code-Review" : 2,
+ * "Verified" : 1
+ * }
+ * }
+ *
+ * </pre>
+ */
+ private Map<Integer, Map<String, Integer>> getLabels(int id) throws RestApiException {
+ ChangeInfo changeInfo =
+ gerritApi.changes().id(id).get(EnumSet.of(ListChangesOption.DETAILED_LABELS));
+
+ Map<Integer, Map<String, Integer>> ownerToLabels = new HashMap<>();
+
+ changeInfo.labels.forEach(
+ (label, labelInfo) -> {
+ Optional.ofNullable(labelInfo.all)
+ .map(
+ approvalInfos -> {
+ approvalInfos.forEach(
+ approvalInfo -> {
+ int currentOwnerId = approvalInfo._accountId;
+ Map<String, Integer> currentOwnerLabels =
+ ownerToLabels.getOrDefault(currentOwnerId, new HashMap<>());
+ currentOwnerLabels.put(label, approvalInfo.value);
+ ownerToLabels.put(currentOwnerId, currentOwnerLabels);
+ });
+ return ownerToLabels;
+ });
+ });
+
+ return ownerToLabels;
+ }
+
private Optional<Owner> getOwnerFromAccountId(Account.Id accountId) {
return accountCache
.get(accountId)
diff --git a/owners/src/main/resources/Documentation/rest-api.md b/owners/src/main/resources/Documentation/rest-api.md
index 2341607..007151a 100644
--- a/owners/src/main/resources/Documentation/rest-api.md
+++ b/owners/src/main/resources/Documentation/rest-api.md
@@ -1,13 +1,32 @@
# Rest API
-The @PLUGIN@ exposes a Rest API endpoint to list the owners associated to each file:
+The @PLUGIN@ exposes a Rest API endpoint to list the owners associated to each file and, for each owner,
+its associated labels and votes:
```bash
GET /changes/{change-id}/revisions/{revision-id}/owners~getFilesOwners
{
- "AJavaFile.java":[{ "name":"John", "id": 1 }, { "name":"Bob", "id": 2 }],
- "Aptyhonfileroot.py":[{ "name":"John", "id": 1 }, { "name":"Bob", "id": 2 }, { "name":"John", "id": 3 }]
+ "files": {
+ "AJavaFile.java":[
+ { "name":"John", "id": 1000002 },
+ { "name":"Bob", "id": 1000001 }
+ ],
+ "Aptyhonfileroot.py":[
+ { "name":"John", "id": 1000002 },
+ { "name":"Bob", "id": 1000001 },
+ { "name":"Jack", "id": 1000003 }
+ ]
+ },
+ "owners_labels" : {
+ "1000002": {
+ "Verified": 1,
+ "Code-Review": 0
+ },
+ "1000001": {
+ "Code-Review": -2
+ }
+ }
}
```
\ No newline at end of file
diff --git a/owners/src/test/java/com/googlesource/gerrit/owners/restapi/GetFilesOwnersIT.java b/owners/src/test/java/com/googlesource/gerrit/owners/restapi/GetFilesOwnersIT.java
index a46f7cf..c1213ab 100644
--- a/owners/src/test/java/com/googlesource/gerrit/owners/restapi/GetFilesOwnersIT.java
+++ b/owners/src/test/java/com/googlesource/gerrit/owners/restapi/GetFilesOwnersIT.java
@@ -23,6 +23,7 @@
import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.server.change.RevisionResource;
+import com.googlesource.gerrit.owners.entities.FilesOwnersResponse;
import com.googlesource.gerrit.owners.entities.Owner;
import java.util.HashMap;
import java.util.Map;
@@ -54,21 +55,37 @@
merge(createChange(testRepo, "master", "Add OWNER file", "OWNERS", getOwnerFileContent(), ""));
PushOneCommit.Result result = createChange();
+
+ approve(result.getChangeId());
+
RevisionResource revisionResource = parseCurrentRevisionResource(result.getChangeId());
Response<?> resp = ownersApi.apply(revisionResource);
assertThat(resp.statusCode()).isEqualTo(HttpServletResponse.SC_OK);
- Map<String, Set<Owner>> responseValue = (Map<String, Set<Owner>>) resp.value();
- HashMap<String, Set<Owner>> expectedResponseValue =
- new HashMap<String, Set<Owner>>() {
- {
- put("a.txt", Sets.newHashSet(new Owner(admin.fullName(), admin.id().get())));
- }
- };
+ FilesOwnersResponse responseValue = (FilesOwnersResponse) resp.value();
- assertThat(responseValue).containsExactlyEntriesIn(expectedResponseValue);
+ FilesOwnersResponse expectedFilesOwnerResponse =
+ new FilesOwnersResponse(
+ new HashMap<Integer, Map<String, Integer>>() {
+ {
+ put(
+ admin.id().get(),
+ new HashMap<String, Integer>() {
+ {
+ put("Code-Review", 2);
+ }
+ });
+ }
+ },
+ new HashMap<String, Set<Owner>>() {
+ {
+ put("a.txt", Sets.newHashSet(new Owner(admin.fullName(), admin.id().get())));
+ }
+ });
+
+ assertThat(responseValue).isEqualTo(expectedFilesOwnerResponse);
}
private String getOwnerFileContent() {