Allow to set plugin configuration parameters on project creation

Change-Id: I478c8d50e143a151bb1e22fc2f0ff2256a62d145
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 3acccc1..b66f18a 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -19,6 +19,7 @@
   [[--branch <REF> | -b <REF>] ...]
   [--empty-commit]
   [--max-object-size-limit <N>]
+  [--plugin-config <PARAM> ...]
   { <NAME> | --name <NAME> }
 --
 
@@ -152,6 +153,14 @@
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
+--plugin-config::
+	A plugin configuration parameter that should be set for this
+	project. The plugin configuration parameter must be specified in
+	the format '<plugin-name>.<parameter-name>=<value>'. Only
+	parameters that are explicitly declared by a plugin can be set.
+	Multiple `--plugin-config` options can be specified to set multiple
+	plugin parameters.
+
 
 == EXAMPLES
 Create a new project called `tools/gerrit`:
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 61f2c15..f126df2 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1533,6 +1533,9 @@
 |`max_object_size_limit`     |optional|
 Max allowed Git object size for this project.
 Common unit suffixes of 'k', 'm', or 'g' are supported.
+|`plugin_config_values`      |optional|
+Plugin configuration values as map which maps the plugin name to a map
+of parameter names to values.
 |=========================================
 
 [[project-parent-input]]
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 32c4ef8..ffcdbef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -21,6 +21,8 @@
 import com.google.gerrit.common.errors.ProjectCreationFailedException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -29,6 +31,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
 import com.google.gerrit.reviewdb.client.Project.SubmitType;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.project.CreateProject.Input;
@@ -41,6 +44,7 @@
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 @RequiresCapability(GlobalCapability.CREATE_PROJECT)
 public class CreateProject implements RestModifyView<TopLevelResource, Input> {
@@ -58,6 +62,7 @@
     public InheritableBoolean useContentMerge;
     public InheritableBoolean requireChangeId;
     public String maxObjectSizeLimit;
+    public Map<String, Map<String, String>> pluginConfigValues;
   }
 
   public static interface Factory {
@@ -68,24 +73,33 @@
   private final Provider<ProjectsCollection> projectsCollection;
   private final Provider<GroupsCollection> groupsCollection;
   private final ProjectJson json;
+  private final ProjectControl.GenericFactory projectControlFactory;
+  private final Provider<CurrentUser> currentUser;
+  private final Provider<PutConfig> putConfig;
   private final String name;
 
   @Inject
   CreateProject(PerformCreateProject.Factory performCreateProjectFactory,
       Provider<ProjectsCollection> projectsCollection,
       Provider<GroupsCollection> groupsCollection, ProjectJson json,
+      ProjectControl.GenericFactory projectControlFactory,
+      Provider<CurrentUser> currentUser, Provider<PutConfig> putConfig,
       @Assisted String name) {
     this.createProjectFactory = performCreateProjectFactory;
     this.projectsCollection = projectsCollection;
     this.groupsCollection = groupsCollection;
     this.json = json;
+    this.projectControlFactory = projectControlFactory;
+    this.currentUser = currentUser;
+    this.putConfig = putConfig;
     this.name = name;
   }
 
   @Override
   public Response<ProjectInfo> apply(TopLevelResource resource, Input input)
       throws BadRequestException, UnprocessableEntityException,
-      ProjectCreationFailedException, IOException {
+      ProjectCreationFailedException, IOException, ResourceNotFoundException,
+      ResourceConflictException {
     if (input == null) {
       input = new Input();
     }
@@ -130,6 +144,19 @@
     }
 
     Project p = createProjectFactory.create(args).createProject();
+
+    if (input.pluginConfigValues != null) {
+      try {
+        ProjectControl projectControl =
+            projectControlFactory.controlFor(p.getNameKey(), currentUser.get());
+        PutConfig.Input in = new PutConfig.Input();
+        in.pluginConfigValues = input.pluginConfigValues;
+        putConfig.get().apply(new ProjectResource(projectControl), in);
+      } catch (NoSuchProjectException e) {
+        throw new ResourceNotFoundException(p.getName());
+      }
+    }
+
     return Response.created(json.format(p));
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index 90c1f2c..f012dac 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -39,7 +39,9 @@
 import org.kohsuke.args4j.Option;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /** Create a new project. **/
 @RequiresCapability(GlobalCapability.CREATE_PROJECT)
@@ -115,6 +117,9 @@
   @Option(name = "--max-object-size-limit", usage = "max Git object size for this project")
   private String maxObjectSizeLimit;
 
+  @Option(name = "--plugin-config", usage = "plugin configuration parameter with format '<plugin-name>.<parameter-name>=<value>'")
+  private List<String> pluginConfigValues;
+
   private String projectName;
 
   @Argument(index = 0, metaVar = "NAME", usage = "name of project to be created")
@@ -164,6 +169,9 @@
         input.branches = branch;
         input.createEmptyCommit = createEmptyCommit;
         input.maxObjectSizeLimit = maxObjectSizeLimit;
+        if (pluginConfigValues != null) {
+          input.pluginConfigValues = parsePluginConfigValues(pluginConfigValues);
+        }
 
         createProjectFactory.get().create(projectName)
             .apply(TopLevelResource.INSTANCE, input);
@@ -180,4 +188,28 @@
       throw new UnloggedFailure(1, "fatal: " + err.getMessage(), err);
     }
   }
+
+  private Map<String, Map<String, String>> parsePluginConfigValues(
+      List<String> pluginConfigValues) throws UnloggedFailure {
+    Map<String, Map<String, String>> m = new HashMap<>();
+    for (String pluginConfigValue : pluginConfigValues) {
+      String[] s = pluginConfigValue.split("=");
+      String[] s2 = s[0].split("\\.");
+      if (s.length != 2 || s2.length != 2) {
+        throw new UnloggedFailure(1, "Invalid plugin config value '"
+            + pluginConfigValue
+            + "', expected format '<plugin-name>.<parameter-name>=<value>'");
+      }
+      String value = s[1];
+      String pluginName = s2[0];
+      String paramName = s2[1];
+      Map<String, String> l = m.get(pluginName);
+      if (l == null) {
+        l = new HashMap<>();
+        m.put(pluginName, l);
+      }
+      l.put(paramName, value);
+    }
+    return m;
+  }
 }