Merge branch 'stable-3.1' into stable-3.2

* stable-3.1:
  Introduce an optional configuration to define a regex for naming rules
  Upgrade bazlets to latest stable-3.1 to build with 3.1.12 API

Change-Id: Ie26bdb0091ec83df53548f0de9718af0abf2b275
diff --git a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/Configuration.java b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/Configuration.java
new file mode 100644
index 0000000..6a3c40f
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/Configuration.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2021 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.ericsson.gerrit.plugins.projectgroupstructure;
+
+import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+class Configuration {
+  private static final Logger log = LoggerFactory.getLogger(Configuration.class);
+
+  private static final String NAME_REGEX = "nameRegex";
+  private static final String DEFAULT_NAME_REGEX_VALUE = ".+";
+  private static final String DEFAULT_NAME_REGEX_MESSAGE = "The value of the regex is invalid.";
+
+  static final String SEE_DOCUMENTATION_MSG = "\n\nSee documentation for more info: %s";
+  static final String DOCUMENTATION_PATH = "Documentation/index.html";
+
+  private String regexNameFilter;
+
+  @Inject
+  Configuration(
+      PluginConfigFactory pluginConfigFactory,
+      @PluginName String pluginName,
+      @PluginCanonicalWebUrl String url) {
+    PluginConfig config = pluginConfigFactory.getFromGerritConfig(pluginName);
+    regexNameFilter = config.getString(NAME_REGEX, DEFAULT_NAME_REGEX_VALUE);
+    if (!"/".matches(regexNameFilter) || " ".matches(regexNameFilter)) {
+      log.warn(
+          String.format(
+              DEFAULT_NAME_REGEX_MESSAGE + SEE_DOCUMENTATION_MSG, url + DOCUMENTATION_PATH));
+      regexNameFilter = DEFAULT_NAME_REGEX_VALUE;
+    }
+  }
+
+  public String getRegexNameFilter() {
+    return regexNameFilter;
+  }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
index 331c968..089ca92 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
@@ -14,6 +14,8 @@
 
 package com.ericsson.gerrit.plugins.projectgroupstructure;
 
+import static com.ericsson.gerrit.plugins.projectgroupstructure.Configuration.SEE_DOCUMENTATION_MSG;
+
 import com.google.common.base.Charsets;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.data.GroupReference;
@@ -51,14 +53,12 @@
   private static final String AN_ERROR_OCCURRED_MSG =
       "An error occurred while creating project, please contact Gerrit support";
 
-  private static final String SEE_DOCUMENTATION_MSG = "\n\nSee documentation for more info: %s";
-
   private static final String MUST_BE_OWNER_TO_CREATE_PROJECT_MSG =
       "You must be owner of the parent project \"%s\" to create a nested project."
           + SEE_DOCUMENTATION_MSG;
 
   private static final String PROJECT_CANNOT_CONTAINS_SPACES_MSG =
-      "Project name cannot contains spaces." + SEE_DOCUMENTATION_MSG;
+      "Project name cannot contain spaces." + SEE_DOCUMENTATION_MSG;
 
   private static final String ROOT_PROJECT_CANNOT_CONTAINS_SLASHES_MSG =
       "Since the \"Rights Inherit From\" field is empty, "
@@ -80,6 +80,9 @@
   private static final String PROJECT_MUST_START_WITH_PARENT_NAME_MSG =
       "Project name must start with parent project name, e.g. %s." + SEE_DOCUMENTATION_MSG;
 
+  private static final String PROJECT_SHOULD_MATCH_REGEX_MSG =
+      "Project name should match the regex: %s." + SEE_DOCUMENTATION_MSG;
+
   static final String DELEGATE_PROJECT_CREATION_TO = "delegateProjectCreationTo";
 
   static final String DISABLE_GRANTING_PROJECT_OWNERSHIP = "disableGrantingProjectOwnership";
@@ -91,6 +94,7 @@
   private final PermissionBackend permissionBackend;
   private final PluginConfigFactory cfg;
   private final String pluginName;
+  private final Configuration config;
 
   @Inject
   public ProjectCreationValidator(
@@ -100,20 +104,27 @@
       Provider<CurrentUser> self,
       PermissionBackend permissionBackend,
       PluginConfigFactory cfg,
+      Configuration config,
       @PluginName String pluginName) {
     this.groups = groups;
-    this.documentationUrl = url + "Documentation/index.html";
+    this.documentationUrl = url + Configuration.DOCUMENTATION_PATH;
     this.allProjectsName = allProjectsName;
     this.self = self;
     this.permissionBackend = permissionBackend;
     this.cfg = cfg;
     this.pluginName = pluginName;
+    this.config = config;
   }
 
   @Override
   public void validateNewProject(CreateProjectArgs args) throws ValidationException {
     String name = args.getProjectName();
     log.debug("validating creation of {}", name);
+    String regex = config.getRegexNameFilter();
+    if (!(name.matches(regex))) {
+      throw new ValidationException(
+          String.format(PROJECT_SHOULD_MATCH_REGEX_MSG, regex, documentationUrl));
+    }
     if (name.contains(" ")) {
       throw new ValidationException(
           String.format(PROJECT_CANNOT_CONTAINS_SPACES_MSG, documentationUrl));
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index ac9a12e..64af68d 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -23,4 +23,17 @@
   label-Code-Review = -2..+2 group ${owner}
   submit = group ${owner}
 
-```
\ No newline at end of file
+```
+Also, this plugin offers a way to restrict the new names of the projects to match an optionally
+configured regex in the gerrit.config. For example:
+
+```
+[plugin "@PLUGIN@"]
+  nameRegex = [a-z0-9/]+
+
+```
+
+In this example, the regex will limit the project created to non-capital letters, numbers
+and slashes. The regex must accept slash (/) to not disturb the functionality of the plugin.
+If the regex doesn't accept / or accepts spaces, it will be ignored and replaced with a default
+non-empty wildcard (.+) regex.
diff --git a/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java b/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
index f85ab27..0f434f7 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
@@ -24,6 +24,8 @@
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.GroupReference;
@@ -47,6 +49,10 @@
   @Inject private ProjectOperations projectOperations;
 
   private static final String PLUGIN_NAME = "project-group-structure";
+  private static final String REGEX_INCLUDING_SLASH = "[a-z_/]+";
+  private static final String REGEX_NOT_INCLUDING_SLASH = "[a-z_]+";
+  private static final String REGEX_INCLUDING_SPACE = "[a-z_/ ]+";
+  private static final String PROJECT_WITH_SPACE = "project with space";
 
   @Override
   @Before
@@ -70,9 +76,44 @@
   public void shouldProjectWithASpaceInTheirName() throws Exception {
     ProjectInput in = new ProjectInput();
     in.permissionsOnly = true;
-    RestResponse r = userRestSession.put("/projects/" + Url.encode("project with space"), in);
+    RestResponse r = userRestSession.put("/projects/" + Url.encode(PROJECT_WITH_SPACE), in);
     r.assertConflict();
-    assertThat(r.getEntityContent()).contains("Project name cannot contains spaces");
+    assertThat(r.getEntityContent()).contains("Project name cannot contain spaces");
+  }
+
+  @Test
+  @UseLocalDisk
+  @GerritConfig(name = "project-group-structure.nameRegex", value = REGEX_INCLUDING_SPACE)
+  public void shouldRejectProjectWithSpaceInItsNameEvenWithRegex() throws Exception {
+    ProjectInput in = new ProjectInput();
+    in.permissionsOnly = true;
+    RestResponse r = userRestSession.put("/projects/" + Url.encode(PROJECT_WITH_SPACE), in);
+    r.assertConflict();
+    assertThat(r.getEntityContent()).contains("Project name cannot contain spaces");
+  }
+
+  @Test
+  @UseLocalDisk
+  @GerritConfig(name = "plugin.project-group-structure.nameRegex", value = REGEX_INCLUDING_SLASH)
+  public void shouldMatchProjectNameIfRegexContainsSlash() throws Exception {
+    ProjectInput in = new ProjectInput();
+    in.permissionsOnly = true;
+    RestResponse r = userRestSession.put("/projects/" + Url.encode("project1"), in);
+    userRestSession.put("/projects/" + Url.encode("project"), in).assertCreated();
+    r.assertConflict();
+    assertThat(r.getEntityContent())
+        .contains(String.format("Project name should match the regex: %s", REGEX_INCLUDING_SLASH));
+  }
+
+  @Test
+  @UseLocalDisk
+  @GerritConfig(
+      name = "plugin.project-group-structure.nameRegex",
+      value = REGEX_NOT_INCLUDING_SLASH)
+  public void shouldNotMatchProjectNameIfRegexMissesSlash() throws Exception {
+    ProjectInput in = new ProjectInput();
+    in.permissionsOnly = true;
+    userRestSession.put("/projects/" + Url.encode("PROJECT1"), in).assertCreated();
   }
 
   @Test