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() {