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 If the commit fails the validation, the plugin can throw an exception
which will cause the merge to fail. 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 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.tools.ToolsCatalog;
import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.server.util.ThreadLocalRequestContext; import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations; import com.google.inject.internal.UniqueAnnotations;
@@ -249,6 +250,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), ChangeListener.class); DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class); DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class); DynamicSet.setOf(binder(), MergeValidationListener.class);
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
DynamicItem.itemOf(binder(), AvatarProvider.class); DynamicItem.itemOf(binder(), AvatarProvider.class);
DynamicSet.setOf(binder(), LifecycleListener.class); DynamicSet.setOf(binder(), LifecycleListener.class);
DynamicSet.setOf(binder(), TopMenu.class); DynamicSet.setOf(binder(), TopMenu.class);

View File

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

View File

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