Support listing code owner config files for a folder in a branch

Allow to specify a path glob for the get code owner config files REST
endpoint so that so that the result can be limited to a folder in the
branch.

Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: Ica6e916cd094a90eb736853428d3918ea67f5f7a
diff --git a/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwners.java b/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwners.java
index 82fb351..22bc543 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwners.java
@@ -34,6 +34,7 @@
   abstract class CodeOwnerConfigFilesRequest {
     private boolean includeNonParsableFiles;
     private String email;
+    private String path;
 
     /** Includes non-parsable code owner config files into the result. */
     public CodeOwnerConfigFilesRequest includeNonParsableFiles(boolean includeNonParsableFiles) {
@@ -62,6 +63,23 @@
       return email;
     }
 
+    /**
+     * Limits the returned code owner config files to those that have a path matching the given
+     * glob.
+     *
+     * @param path the path glob that should be matched
+     */
+    public CodeOwnerConfigFilesRequest withPath(String path) {
+      this.path = path;
+      return this;
+    }
+
+    /** Returns the path glob that should be matched by the returned code owner config files/ */
+    @Nullable
+    public String getPath() {
+      return path;
+    }
+
     /** Executes the request and retrieves the paths of the requested code owner config file */
     public abstract List<String> paths() throws RestApiException;
   }
diff --git a/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwnersImpl.java b/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwnersImpl.java
index f6e4804..7052a1b 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwnersImpl.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/BranchCodeOwnersImpl.java
@@ -66,6 +66,7 @@
         GetCodeOwnerConfigFiles getCodeOwnerConfigFiles = getCodeOwnerConfigFilesProvider.get();
         getCodeOwnerConfigFiles.setIncludeNonParsableFiles(getIncludeNonParsableFiles());
         getCodeOwnerConfigFiles.setEmail(getEmail());
+        getCodeOwnerConfigFiles.setPath(getPath());
         return getCodeOwnerConfigFiles.apply(branchResource).value();
       }
     };
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnerConfigFiles.java b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnerConfigFiles.java
index 8190e7c..9ec7118 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnerConfigFiles.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnerConfigFiles.java
@@ -51,6 +51,7 @@
 
   private boolean includeNonParsableFiles;
   private String email;
+  private String pathGlob;
 
   @Option(
       name = "--include-non-parsable-files",
@@ -67,6 +68,15 @@
     this.email = email;
   }
 
+  @Option(
+      name = "--path",
+      usage =
+          "limits the returned code owner config files to those that have a path matching"
+              + " this glob")
+  public void setPath(@Nullable String pathGlob) {
+    this.pathGlob = pathGlob;
+  }
+
   @Inject
   public GetCodeOwnerConfigFiles(
       CodeOwnersPluginConfiguration codeOwnersPluginConfiguration,
@@ -108,7 +118,8 @@
                 ? (codeOwnerConfigFilePath, configInvalidException) -> {
                   codeOwnerConfigs.add(codeOwnerConfigFilePath);
                 }
-                : CodeOwnerConfigScanner.ignoreInvalidCodeOwnerConfigFiles());
+                : CodeOwnerConfigScanner.ignoreInvalidCodeOwnerConfigFiles(),
+            pathGlob);
     return Response.ok(
         codeOwnerConfigs.build().stream().map(Path::toString).collect(toImmutableList()));
   }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigFilesIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigFilesIT.java
index 284f01b..31d1b87 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigFilesIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigFilesIT.java
@@ -373,6 +373,68 @@
         .isEmpty();
   }
 
+  @Test
+  public void getCodeOwnerConfigFilesWithPathGlob() throws Exception {
+    codeOwnerConfigOperations
+        .newCodeOwnerConfig()
+        .project(project)
+        .branch("master")
+        .folderPath("/")
+        .addCodeOwnerEmail(user.email())
+        .create();
+
+    CodeOwnerConfig.Key codeOwnerConfigKey2 =
+        codeOwnerConfigOperations
+            .newCodeOwnerConfig()
+            .project(project)
+            .branch("master")
+            .folderPath("/foo/")
+            .addCodeOwnerEmail(admin.email())
+            .create();
+
+    CodeOwnerConfig.Key codeOwnerConfigKey3 =
+        codeOwnerConfigOperations
+            .newCodeOwnerConfig()
+            .project(project)
+            .branch("master")
+            .folderPath("/foo/bar/")
+            .addCodeOwnerEmail(user.email())
+            .create();
+
+    assertThat(
+            projectCodeOwnersApiFactory
+                .project(project)
+                .branch("master")
+                .codeOwnerConfigFiles()
+                .withPath("/foo/bar/" + getCodeOwnerConfigFileName())
+                .paths())
+        .containsExactly(
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey3).getFilePath());
+
+    assertThat(
+            projectCodeOwnersApiFactory
+                .project(project)
+                .branch("master")
+                .codeOwnerConfigFiles()
+                .withPath("/foo/bar/*")
+                .paths())
+        .containsExactly(
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey3).getFilePath())
+        .inOrder();
+
+    assertThat(
+            projectCodeOwnersApiFactory
+                .project(project)
+                .branch("master")
+                .codeOwnerConfigFiles()
+                .withPath("/foo/**")
+                .paths())
+        .containsExactly(
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey2).getFilePath(),
+            codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey3).getFilePath())
+        .inOrder();
+  }
+
   private String getCodeOwnerConfigFileName() {
     CodeOwnerBackend backend = backendConfig.getDefaultBackend();
     if (backend instanceof FindOwnersBackend) {
diff --git a/resources/Documentation/rest-api.md b/resources/Documentation/rest-api.md
index 64a077e..03ccd87 100644
--- a/resources/Documentation/rest-api.md
+++ b/resources/Documentation/rest-api.md
@@ -188,6 +188,7 @@
 | ----------- | -------- | ----------- |
 | `include-non-parsable-files` | optional | Includes non-parseable code owner config files in the response. By default `false`. Cannot be used in combination with the `email` option.
 | `email`     | optional | Code owner email that must appear in the returned code owner config files.
+| `path`      | optional | Path glob that must be matched by the returned code owner config files.
 
 #### Request