Add REST API for 'create project'

Change-Id: Ifbeb2b02ece00bf7210cb21bdd41a26d2e10450d
This commit is contained in:
Bruce Zu 2013-02-18 16:55:43 +08:00 committed by Edwin Kempin
parent 1d028b8f60
commit 798ea128a4
15 changed files with 433 additions and 40 deletions

View File

@ -129,6 +129,50 @@ describes the project.
}
----
[[create-project]]
Create Project
~~~~~~~~~~~~~~
[verse]
'PUT /projects/link:#project-name[\{project-name\}]'
Creates a new project.
In the request body additional data for the project can be provided as
link:#project-input[ProjectInput].
.Request
----
PUT /projects/MyProject HTTP/1.0
Content-Type: application/json;charset=UTF-8
{
"description": "This is a demo project.",
"submit_type": "CHERRY_PICK",
"owners": [
"MyProject-Owners"
]
}
----
As response the link:#project-info[ProjectInfo] entity is returned that
describes the created project.
.Response
----
HTTP/1.1 201 Created
Content-Disposition: attachment
Content-Type: application/json;charset=UTF-8
)]}'
{
"kind": "gerritcodereview#project",
"id": "MyProject",
"name": "MyProject",
"parent": "All-Projects",
"description": "This is a demo project."
}
----
[[get-project-description]]
Get Project Description
~~~~~~~~~~~~~~~~~~~~~~~
@ -627,6 +671,56 @@ is increased for each non-visible project).
|`branches` |optional|Map of branch names to HEAD revisions.
|===========================
[[project-input]]
ProjectInput
~~~~~~~~~~~~
The `ProjectInput` entity contains information for the creation of
a new project.
[options="header",width="50%",cols="1,^2,4"]
|=========================================
|Field Name ||Description
|`name` |optional|
The name of the project (not encoded). +
If set, must match the project name in the URL.
|`parent` |optional|
The name of the parent project. +
If not set, the `All-Projects` project will be the parent project.
|`description` |optional|The description of the project.
|`permissions_only` |`false` if not set|
Whether a permission-only project should be created.
|`create_empty_commit` |`false` if not set|
Whether an empty initial commit should be created.
|`submit_type` |optional|
The submit type that should be set for the project
(`MERGE_IF_NECESSARY`, `REBASE_IF_NECESSARY`, `FAST_FORWARD_ONLY`,
`MERGE_ALWAYS`, `CHERRY_PICK`). +
If not set, `MERGE_IF_NECESSARY` is set as submit type.
|`branches` |optional|
A list of branches that should be initially created. +
For the branch names the `refs/heads/` prefix can be omitted.
|`owners` |optional|
A list of groups that should be assigned as project owner. +
Each group in the list must be specified as
link:rest-api-groups.html#group-id[group-id]. +
If not set, the link:config-gerrit.html#repository.name.ownerGroup[
groups that are configured as default owners] are set as project
owners.
|`use_contributor_agreements`|`INHERIT` if not set|
Whether contributor agreements should be used for the project (`TRUE`,
`FALSE`, `INHERIT`).
|`use_signed_off_by` |`INHERIT` if not set|
Whether the usage of 'Signed-Off-By' footers is required for the
project (`TRUE`, `FALSE`, `INHERIT`).
|`use_content_merge` |`INHERIT` if not set|
Whether content merge should be enabled for the project (`TRUE`,
`FALSE`, `INHERIT`). +
`FALSE`, if the `submit_type` is `FAST_FORWARD_ONLY`.
|`require_change_id` |`INHERIT` if not set|
Whether the usage of Change-Ids is required for the project (`TRUE`,
`FALSE`, `INHERIT`).
|=========================================
[[project-parent-input]]
ProjectParentInput
~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,24 @@
// Copyright (C) 2013 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.extensions.restapi;
/** Resource referenced in the request body is not found (HTTP 422 Unprocessable Entity). */
public class UnprocessableEntityException extends RestApiException {
private static final long serialVersionUID = 1L;
public UnprocessableEntityException(String msg) {
super(msg);
}
}

View File

@ -20,7 +20,9 @@ import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.NotFoundScreen;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.account.AccountCapabilities;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.projects.ProjectInfo;
import com.google.gerrit.client.projects.ProjectMap;
import com.google.gerrit.client.rpc.GerritCallback;
@ -38,6 +40,7 @@ import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
@ -45,7 +48,6 @@ import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwtexpui.globalkey.client.NpTextBox;
import com.google.gwtjsonrpc.common.VoidResult;
public class CreateProjectScreen extends Screen {
private Grid grid;
@ -231,9 +233,8 @@ public class CreateProjectScreen extends Screen {
}
enableForm(false);
Util.PROJECT_SVC.createNewProject(projectName, parentName,
emptyCommit.getValue(), permissionsOnly.getValue(),
new GerritCallback<VoidResult>() {
ProjectApi.createProject(projectName, parentName, emptyCommit.getValue(),
permissionsOnly.getValue(), new AsyncCallback<VoidResult>() {
@Override
public void onSuccess(VoidResult result) {
String nameWithoutSuffix = ProjectUtil.stripGitSuffix(projectName);

View File

@ -0,0 +1,51 @@
// Copyright (C) 2013 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.client.projects;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class ProjectApi {
/** Create a new project */
public static void createProject(String projectName, String parent,
Boolean createEmptyCcommit, Boolean permissionsOnly,
AsyncCallback<VoidResult> asyncCallback) {
ProjectInput input = ProjectInput.create();
input.setName(projectName);
input.setParent(parent);
input.setPermissionsOnly(permissionsOnly);
input.setCreateEmptyCommit(createEmptyCcommit);
new RestApi("/projects/").id(projectName).ifNoneMatch()
.put(input, asyncCallback);
}
private static class ProjectInput extends JavaScriptObject {
static ProjectInput create() {
return (ProjectInput) createObject();
}
protected ProjectInput() {
}
final native void setName(String n) /*-{ if(n)this.name=n; }-*/;
final native void setParent(String p) /*-{ if(p)this.parent=p; }-*/;
final native void setPermissionsOnly(boolean po) /*-{ if(po)this.permissions_only=po; }-*/;
final native void setCreateEmptyCommit(boolean cc) /*-{ if(cc)this.create_empty_commit=cc; }-*/;
}
}

View File

@ -62,6 +62,7 @@ import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
@ -319,6 +320,9 @@ public class RestApiServlet extends HttpServlet {
Objects.firstNonNull(e.getMessage(), "Precondition failed"));
} catch (ResourceNotFoundException e) {
replyError(res, status = SC_NOT_FOUND, "Not found");
} catch (UnprocessableEntityException e) {
replyError(res, status = 422,
Objects.firstNonNull(e.getMessage(), "Unprocessable Entity"));
} catch (AmbiguousViewException e) {
replyError(res, status = SC_NOT_FOUND, e.getMessage());
} catch (MalformedJsonException e) {

View File

@ -91,6 +91,7 @@ import com.google.gerrit.server.patch.PatchListCacheImpl;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.AccessControlModule;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.PerformCreateProject;
import com.google.gerrit.server.project.PermissionCollection;
import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.project.ProjectControl;
@ -185,6 +186,7 @@ public class GerritGlobalModule extends FactoryModule {
factory(ProjectState.Factory.class);
factory(RebasedPatchSetSender.Factory.class);
factory(ReplacePatchSetSender.Factory.class);
factory(PerformCreateProject.Factory.class);
bind(PermissionCollection.Factory.class);
bind(AccountVisibility.class)
.toProvider(AccountVisibilityProvider.class)

View File

@ -23,11 +23,9 @@ import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
import com.google.gerrit.server.changedetail.PublishDraft;
import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.SubmoduleOp;
import com.google.gerrit.server.patch.RemoveReviewer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.PerformCreateProject;
import com.google.gerrit.server.project.PerRequestProjectControlCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.SuggestParentCandidates;
@ -40,7 +38,6 @@ public class GerritRequestModule extends FactoryModule {
bind(RequestCleanup.class).in(RequestScoped.class);
bind(RequestScopedReviewDbProvider.class);
bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
bind(MetaDataUpdate.User.class).in(RequestScoped.class);
bind(ApprovalsUtil.class);
bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
@ -56,7 +53,6 @@ public class GerritRequestModule extends FactoryModule {
factory(DeleteDraftPatchSet.Factory.class);
factory(PublishDraft.Factory.class);
factory(RemoveReviewer.Factory.class);
factory(PerformCreateProject.Factory.class);
factory(SuggestParentCandidates.Factory.class);
factory(BanCommit.Factory.class);
}

View File

@ -0,0 +1,136 @@
// Copyright (C) 2013 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.project;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
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.restapi.BadRequestException;
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;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
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.group.GroupsCollection;
import com.google.gerrit.server.project.CreateProject.Input;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.util.List;
@RequiresCapability(GlobalCapability.CREATE_PROJECT)
class CreateProject implements RestModifyView<TopLevelResource, Input> {
static class Input {
String name;
String parent;
String description;
boolean permissionsOnly;
boolean createEmptyCommit;
SubmitType submitType;
List<String> branches;
List<String> owners;
InheritableBoolean useContributorAgreements;
InheritableBoolean useSignedOffBy;
InheritableBoolean useContentMerge;
InheritableBoolean requireChangeId;
}
static interface Factory {
CreateProject create(String name);
}
private final PerformCreateProject.Factory createProjectFactory;
private final Provider<ProjectsCollection> projectsCollection;
private final Provider<GroupsCollection> groupsCollection;
private final ProjectJson json;
private final String name;
@Inject
CreateProject(PerformCreateProject.Factory performCreateProjectFactory,
Provider<ProjectsCollection> projectsCollection,
Provider<GroupsCollection> groupsCollection, ProjectJson json,
@Assisted String name) {
this.createProjectFactory = performCreateProjectFactory;
this.projectsCollection = projectsCollection;
this.groupsCollection = groupsCollection;
this.json = json;
this.name = name;
}
@Override
public Object apply(TopLevelResource resource, Input input)
throws BadRequestException, UnprocessableEntityException,
ProjectCreationFailedException {
if (input == null) {
input = new Input();
}
if (input.name != null && !name.equals(input.name)) {
throw new BadRequestException("name must match URL");
}
final CreateProjectArgs args = new CreateProjectArgs();
args.setProjectName(name);
if (!Strings.isNullOrEmpty(input.parent)) {
try {
args.newParent =
projectsCollection.get().parse(input.parent).getControl();
} catch (ResourceNotFoundException e) {
throw new UnprocessableEntityException(String.format(
"parent project \"%s\" not found", input.parent));
}
}
args.createEmptyCommit = input.createEmptyCommit;
args.permissionsOnly = input.permissionsOnly;
args.projectDescription = Strings.emptyToNull(input.description);
args.submitType =
Objects.firstNonNull(input.submitType, SubmitType.MERGE_IF_NECESSARY);
args.branch = input.branches;
if (input.owners != null) {
List<AccountGroup.UUID> ownerIds =
Lists.newArrayListWithCapacity(input.owners.size());
for (String owner : input.owners) {
try {
ownerIds.add(groupsCollection.get().parse(owner).getGroupUUID());
} catch (ResourceNotFoundException e) {
throw new UnprocessableEntityException(String.format(
"group \"%s\" not found", owner));
}
}
args.ownerIds = ownerIds;
}
args.contributorAgreements =
Objects.firstNonNull(input.useContributorAgreements,
InheritableBoolean.INHERIT);
args.signedOffBy =
Objects.firstNonNull(input.useSignedOffBy, InheritableBoolean.INHERIT);
args.contentMerge =
input.submitType == SubmitType.FAST_FORWARD_ONLY
? InheritableBoolean.FALSE : Objects.firstNonNull(
input.useContentMerge, InheritableBoolean.INHERIT);
args.changeIdRequired =
Objects.firstNonNull(input.requireChangeId, InheritableBoolean.INHERIT);
Project p = createProjectFactory.create(args).createProject();
return Response.created(json.format(p));
}
}

View File

@ -41,6 +41,7 @@ public class CreateProjectArgs {
signedOffBy = InheritableBoolean.INHERIT;
contentMerge = InheritableBoolean.INHERIT;
changeIdRequired = InheritableBoolean.INHERIT;
submitType = SubmitType.MERGE_IF_NECESSARY;
}
public Project.NameKey getProject() {

View File

@ -14,43 +14,20 @@
package com.google.gerrit.server.project;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.inject.Inject;
class GetProject implements RestReadView<ProjectResource> {
private final AllProjectsName allProjects;
private final ProjectJson json;
@Inject
GetProject(AllProjectsName allProjects) {
this.allProjects = allProjects;
GetProject(ProjectJson json) {
this.json = json;
}
@Override
public Object apply(ProjectResource resource) {
Project project = resource.getControl().getProject();
ProjectInfo info = new ProjectInfo();
info.name = resource.getName();
Project.NameKey parentName = project.getParent(allProjects);
info.parent = parentName != null ? parentName.get() : null;
info.description = Strings.emptyToNull(project.getDescription());
info.finish();
return info;
}
static class ProjectInfo {
final String kind = "gerritcodereview#project";
String id;
String name;
String parent;
String description;
void finish() {
id = Url.encode(name);
}
public Object apply(ProjectResource rsrc) {
return json.format(rsrc);
}
}

View File

@ -19,6 +19,8 @@ import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.server.project.CreateProject;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class Module extends RestApiModule {
@Override
@ -29,6 +31,7 @@ public class Module extends RestApiModule {
DynamicMap.mapOf(binder(), PROJECT_KIND);
DynamicMap.mapOf(binder(), DASHBOARD_KIND);
put(PROJECT_KIND).to(PutProject.class);
get(PROJECT_KIND).to(GetProject.class);
get(PROJECT_KIND, "description").to(GetDescription.class);
put(PROJECT_KIND, "description").to(PutDescription.class);
@ -41,5 +44,6 @@ public class Module extends RestApiModule {
get(DASHBOARD_KIND).to(GetDashboard.class);
put(DASHBOARD_KIND).to(SetDashboard.class);
delete(DASHBOARD_KIND).to(DeleteDashboard.class);
install(new FactoryModuleBuilder().build(CreateProject.Factory.class));
}
}

View File

@ -99,7 +99,7 @@ public class PerformCreateProject {
this.metaDataUpdateFactory = metaDataUpdateFactory;
}
public void createProject() throws ProjectCreationFailedException {
public Project createProject() throws ProjectCreationFailedException {
validateParameters();
final Project.NameKey nameKey = createProjectArgs.getProject();
try {
@ -133,6 +133,8 @@ public class PerformCreateProject {
&& createProjectArgs.createEmptyCommit) {
createEmptyCommits(repo, nameKey, createProjectArgs.branch);
}
return projectCache.get(nameKey).getProject();
} finally {
repo.close();
}
@ -150,6 +152,7 @@ public class PerformCreateProject {
if (repo.getObjectDatabase().exists()) {
throw new ProjectCreationFailedException("project \"" + nameKey + "\" exists");
}
throw err;
} finally {
repo.close();
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2013 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.project;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.inject.Inject;
public class ProjectJson {
private final AllProjectsName allProjects;
@Inject
ProjectJson(AllProjectsName allProjects) {
this.allProjects = allProjects;
}
public ProjectInfo format(ProjectResource rsrc) {
return format(rsrc.getControl().getProject());
}
public ProjectInfo format(Project p) {
ProjectInfo info = new ProjectInfo();
info.name = p.getName();
Project.NameKey parentName = p.getParent(allProjects);
info.parent = parentName != null ? parentName.get() : null;
info.description = Strings.emptyToNull(p.getDescription());
info.finish();
return info;
}
static class ProjectInfo {
final String kind = "gerritcodereview#project";
String id;
String name;
String parent;
String description;
void finish() {
id = Url.encode(name);
}
}
}

View File

@ -15,6 +15,7 @@
package com.google.gerrit.server.project;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestCollection;
@ -27,21 +28,24 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
public class ProjectsCollection implements
RestCollection<TopLevelResource, ProjectResource> {
RestCollection<TopLevelResource, ProjectResource>,
AcceptsCreate<TopLevelResource> {
private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<ListProjects> list;
private final ProjectControl.GenericFactory controlFactory;
private final Provider<CurrentUser> user;
private final CreateProject.Factory createProjectFactory;
@Inject
ProjectsCollection(DynamicMap<RestView<ProjectResource>> views,
Provider<ListProjects> list,
ProjectControl.GenericFactory controlFactory,
Provider<CurrentUser> user) {
CreateProject.Factory factory, Provider<CurrentUser> user) {
this.views = views;
this.list = list;
this.controlFactory = controlFactory;
this.user = user;
this.createProjectFactory = factory;
}
@Override
@ -52,10 +56,15 @@ public class ProjectsCollection implements
@Override
public ProjectResource parse(TopLevelResource parent, IdString id)
throws ResourceNotFoundException {
return parse(id.get());
}
public ProjectResource parse(String id)
throws ResourceNotFoundException {
ProjectControl ctl;
try {
ctl = controlFactory.controlFor(
new Project.NameKey(id.get()),
new Project.NameKey(id),
user.get());
} catch (NoSuchProjectException e) {
throw new ResourceNotFoundException(id);
@ -70,4 +79,10 @@ public class ProjectsCollection implements
public DynamicMap<RestView<ProjectResource>> views() {
return views;
}
@SuppressWarnings("unchecked")
@Override
public CreateProject create(TopLevelResource parent, IdString name) {
return createProjectFactory.create(name.get());
}
}

View File

@ -0,0 +1,28 @@
// Copyright (C) 2013 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.project;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.project.CreateProject.Input;
public class PutProject implements RestModifyView<ProjectResource, Input> {
@Override
public Object apply(ProjectResource resource, Input input)
throws ResourceConflictException {
throw new ResourceConflictException("Project \"" + resource.getName()
+ "\" already exists");
}
}