Add extension point to allow plugins to validate project creation

By implementing this extension point plugins can validate the creation
of new projects based on input arguments. E.g. a plugin could enforce
a certain name scheme for the project names. A more sophisticated
plugin could use this extension point to restrict the project creation
for certain groups to certain folders.

Change-Id: I6b5c69cd435acf9789111d13f31049ef38699215
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2014-01-22 16:13:03 +01:00
parent 0623c5b8ea
commit de3bfb2fb5
7 changed files with 96 additions and 3 deletions

View File

@@ -32,6 +32,17 @@ are merged to the git repository.
If the commit fails the validation, the plugin can throw an exception
which will cause the merge to fail.
[[new-project-validation]]
== New project validation
Plugins implementing the `ProjectCreationValidationListener` interface
can perform additional validation on project creation based on the
input arguments.
E.g. a plugin could use this to enforce a certain name scheme for
project names.
GERRIT
------

View File

@@ -118,6 +118,7 @@ import com.google.gerrit.server.ssh.SshAddressesModule;
import com.google.gerrit.server.tools.ToolsCatalog;
import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
@@ -249,6 +250,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class);
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
DynamicItem.itemOf(binder(), AvatarProvider.class);
DynamicSet.setOf(binder(), LifecycleListener.class);
DynamicSet.setOf(binder(), TopMenu.class);

View File

@@ -14,10 +14,12 @@
package com.google.gerrit.server.git.validators;
import com.google.gerrit.server.validators.ValidationException;
import java.util.Collections;
import java.util.List;
public class CommitValidationException extends Exception {
public class CommitValidationException extends ValidationException {
private static final long serialVersionUID = 1L;
private final List<CommitValidationMessage> messages;

View File

@@ -15,8 +15,9 @@
package com.google.gerrit.server.git.validators;
import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.validators.ValidationException;
public class MergeValidationException extends Exception {
public class MergeValidationException extends ValidationException {
private static final long serialVersionUID = 1L;
private final CommitMergeStatus status;

View File

@@ -20,7 +20,9 @@ import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.ProjectCreationFailedException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.registration.DynamicSet;
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.RestModifyView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -33,6 +35,8 @@ import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.group.GroupsCollection;
import com.google.gerrit.server.project.CreateProject.Input;
import com.google.gerrit.server.project.ProjectJson.ProjectInfo;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -67,6 +71,7 @@ public class CreateProject implements RestModifyView<TopLevelResource, Input> {
private final PerformCreateProject.Factory createProjectFactory;
private final Provider<ProjectsCollection> projectsCollection;
private final Provider<GroupsCollection> groupsCollection;
private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
private final ProjectJson json;
private final String name;
@@ -74,10 +79,12 @@ public class CreateProject implements RestModifyView<TopLevelResource, Input> {
CreateProject(PerformCreateProject.Factory performCreateProjectFactory,
Provider<ProjectsCollection> projectsCollection,
Provider<GroupsCollection> groupsCollection, ProjectJson json,
DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners,
@Assisted String name) {
this.createProjectFactory = performCreateProjectFactory;
this.projectsCollection = projectsCollection;
this.groupsCollection = groupsCollection;
this.projectCreationValidationListeners = projectCreationValidationListeners;
this.json = json;
this.name = name;
}
@@ -85,7 +92,7 @@ public class CreateProject implements RestModifyView<TopLevelResource, Input> {
@Override
public Response<ProjectInfo> apply(TopLevelResource resource, Input input)
throws BadRequestException, UnprocessableEntityException,
ProjectCreationFailedException, IOException {
ResourceConflictException, ProjectCreationFailedException, IOException {
if (input == null) {
input = new Input();
}
@@ -129,6 +136,14 @@ public class CreateProject implements RestModifyView<TopLevelResource, Input> {
throw new BadRequestException(e.getMessage());
}
for (ProjectCreationValidationListener l : projectCreationValidationListeners) {
try {
l.validateNewProject(args);
} catch (ValidationException e) {
throw new ResourceConflictException(e.getMessage(), e);
}
}
Project p = createProjectFactory.create(args).createProject();
return Response.created(json.format(p));
}

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2014 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.google.gerrit.server.validators;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.server.project.CreateProjectArgs;
/**
* Listener to provide validation on project creation.
*/
@ExtensionPoint
public interface ProjectCreationValidationListener {
/**
* Project creation validation.
*
* Invoked by Gerrit just before a new project is going to be created.
*
* @param args arguments for the project creation
* @throws ValidationException if validation fails
*/
public void validateNewProject(CreateProjectArgs args)
throws ValidationException;
}

View File

@@ -0,0 +1,27 @@
// Copyright (C) 2014 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.google.gerrit.server.validators;
public class ValidationException extends Exception {
private static final long serialVersionUID = 1L;
public ValidationException(String reason) {
super(reason);
}
public ValidationException(String reason, Throwable why) {
super(reason, why);
}
}