From 6b6afe2681dd187f144a10703b9c75f7792fa710 Mon Sep 17 00:00:00 2001 From: Edwin Kempin Date: Mon, 18 Jun 2018 13:23:33 +0200 Subject: [PATCH] RestApiModule: Support binding a RestView for resource creation Instead of handling resource creation by implementing AcceptsCreate in the RestCollection, add a new RestCreateView that can be bound via RestApiModule. This improves code readability since by reading the Module class we can now directly see which REST collections support resource creation. In addition we no longer need factories to create the REST view that creates the resource. This allows us at Google to bind a different REST view for the resource creation internally while we still use the upstream REST collection for parsing and listing. Without this change we would need to subclass the upstream RestCollection and override the create method which is error-prone. This change adds REST tests for creating a resource on a root collection (com.google.gerrit.acceptance.rest.account.CreateAccountIT#createAccountRestApi) and for creating a resource on a child collection (com.google.gerrit.acceptance.rest.account.CreateBranchIT#createBranchRestApi), however it doesn't test resource creation via REST for all possible resource types. There are more things that we likely also want to replace by bindings (AcceptsPost, AcceptsDelete, listing members). This should be done by follow-up changes. Change-Id: I5cd61f77aad2a59a02333b5f68b86bda6c353879 Signed-off-by: Edwin Kempin --- .../extensions/restapi/AcceptsCreate.java | 34 ---------- .../extensions/restapi/RestApiModule.java | 39 +++++++++++ .../extensions/restapi/RestCreateView.java | 45 +++++++++++++ .../gerrit/httpd/restapi/RestApiServlet.java | 65 +++++++++++++++---- .../server/api/accounts/AccountApiImpl.java | 15 ++--- .../server/api/accounts/AccountsImpl.java | 14 ++-- .../gerrit/server/api/groups/GroupsImpl.java | 12 ++-- .../server/api/projects/BranchApiImpl.java | 8 +-- .../server/api/projects/ProjectApiImpl.java | 21 +++--- .../server/api/projects/TagApiImpl.java | 8 +-- .../gerrit/server/plugins/InstallPlugin.java | 22 +++++++ .../server/plugins/PluginRestApiModule.java | 1 + .../server/plugins/PluginsCollection.java | 18 +---- .../restapi/account/AccountsCollection.java | 15 +---- .../server/restapi/account/CreateAccount.java | 42 ++++++------ .../server/restapi/account/CreateEmail.java | 24 +++---- .../restapi/account/EmailsCollection.java | 15 +---- .../gerrit/server/restapi/account/Module.java | 5 +- .../restapi/account/StarredChanges.java | 50 +++++++------- .../server/restapi/change/ChangeEdits.java | 27 ++------ .../gerrit/server/restapi/change/Module.java | 2 +- .../server/restapi/group/AddMembers.java | 14 ++-- .../server/restapi/group/AddSubgroups.java | 15 +++-- .../server/restapi/group/CreateGroup.java | 17 ++--- .../restapi/group/GroupsCollection.java | 14 +--- .../restapi/group/MembersCollection.java | 15 +---- .../gerrit/server/restapi/group/Module.java | 6 +- .../restapi/group/SubgroupsCollection.java | 15 +---- .../restapi/project/BranchesCollection.java | 14 +--- .../server/restapi/project/CreateBranch.java | 19 ++---- .../restapi/project/CreateDashboard.java | 58 +++++++++++++++++ .../server/restapi/project/CreateProject.java | 20 +++--- .../server/restapi/project/CreateTag.java | 20 ++---- .../restapi/project/DashboardsCollection.java | 18 +---- .../gerrit/server/restapi/project/Module.java | 7 +- .../restapi/project/ProjectsCollection.java | 13 +--- .../restapi/project/SetDefaultDashboard.java | 23 +------ .../restapi/project/TagsCollection.java | 16 +---- .../sshd/commands/CreateAccountCommand.java | 5 +- .../sshd/commands/CreateGroupCommand.java | 5 +- .../sshd/commands/SetAccountCommand.java | 5 +- .../rest/account/CreateAccountIT.java | 34 ++++++++++ .../rest/change/SuggestReviewersIT.java | 6 +- .../rest/project/CreateBranchIT.java | 18 ++++- 44 files changed, 455 insertions(+), 404 deletions(-) delete mode 100644 java/com/google/gerrit/extensions/restapi/AcceptsCreate.java create mode 100644 java/com/google/gerrit/extensions/restapi/RestCreateView.java create mode 100644 java/com/google/gerrit/server/restapi/project/CreateDashboard.java create mode 100644 javatests/com/google/gerrit/acceptance/rest/account/CreateAccountIT.java diff --git a/java/com/google/gerrit/extensions/restapi/AcceptsCreate.java b/java/com/google/gerrit/extensions/restapi/AcceptsCreate.java deleted file mode 100644 index 994e7f28f1..0000000000 --- a/java/com/google/gerrit/extensions/restapi/AcceptsCreate.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2012 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; - -/** - * Optional interface for {@link RestCollection}. - * - *

Collections that implement this interface can accept a {@code PUT} or {@code POST} when the - * parse method throws {@link ResourceNotFoundException}. - */ -public interface AcceptsCreate

{ - /** - * Handle creation of a child resource. - * - * @param parent parent collection handle. - * @param id id of the resource being created. - * @return a view to perform the creation. The create method must embed the id into the newly - * returned view object, as it will not be passed. - * @throws RestApiException the view cannot be constructed. - */ - RestModifyView create(P parent, IdString id) throws RestApiException; -} diff --git a/java/com/google/gerrit/extensions/restapi/RestApiModule.java b/java/com/google/gerrit/extensions/restapi/RestApiModule.java index 0db28917b8..783df0b7ff 100644 --- a/java/com/google/gerrit/extensions/restapi/RestApiModule.java +++ b/java/com/google/gerrit/extensions/restapi/RestApiModule.java @@ -28,6 +28,7 @@ public abstract class RestApiModule extends FactoryModule { protected static final String PUT = "PUT"; protected static final String DELETE = "DELETE"; protected static final String POST = "POST"; + protected static final String CREATE = "CREATE"; protected ReadViewBinder get(TypeLiteral> viewType) { return new ReadViewBinder<>(view(viewType, GET, "/")); @@ -45,6 +46,11 @@ public abstract class RestApiModule extends FactoryModule { return new ModifyViewBinder<>(view(viewType, DELETE, "/")); } + protected

CreateViewBinder create( + TypeLiteral> viewType) { + return new CreateViewBinder<>(createView(viewType, CREATE, "/")); + } + protected ReadViewBinder get( TypeLiteral> viewType, String name) { return new ReadViewBinder<>(view(viewType, GET, name)); @@ -75,6 +81,12 @@ public abstract class RestApiModule extends FactoryModule { return bind(viewType).annotatedWith(export(method, name)); } + protected

+ LinkedBindingBuilder> createView( + TypeLiteral> viewType, String method, String name) { + return bind(viewType).annotatedWith(export(method, name)); + } + private static Export export(String method, String name) { if (name.length() > 1 && name.startsWith("/")) { // Views may be bound as "/" to mean the resource itself, or @@ -137,6 +149,33 @@ public abstract class RestApiModule extends FactoryModule { } } + public static class CreateViewBinder { + private final LinkedBindingBuilder> binder; + + private CreateViewBinder(LinkedBindingBuilder> binder) { + this.binder = binder; + } + + public

> ScopedBindingBuilder to( + Class impl) { + return binder.to(impl); + } + + public

> void toInstance(T impl) { + binder.toInstance(impl); + } + + public

> + ScopedBindingBuilder toProvider(Class> providerType) { + return binder.toProvider(providerType); + } + + public

> + ScopedBindingBuilder toProvider(Provider provider) { + return binder.toProvider(provider); + } + } + public static class ChildCollectionBinder

{ private final LinkedBindingBuilder> binder; diff --git a/java/com/google/gerrit/extensions/restapi/RestCreateView.java b/java/com/google/gerrit/extensions/restapi/RestCreateView.java new file mode 100644 index 0000000000..5278deb0a9 --- /dev/null +++ b/java/com/google/gerrit/extensions/restapi/RestCreateView.java @@ -0,0 +1,45 @@ +// Copyright (C) 2018 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; + +/** + * RestView that supports accepting input and creating a resource. + * + *

The input must be supplied as JSON as the body of the HTTP request. Create views can be + * invoked by the HTTP methods {@code PUT} and {@code POST}. + * + *

The RestCreateView is only invoked when the parse method of the {@code RestCollection} throws + * {@link ResourceNotFoundException}, and hence the resource doesn't exist yet. + * + * @param

type of the parent resource + * @param type of the child resource that is created + * @param type of input the JSON parser will parse the input into. + */ +public interface RestCreateView

+ extends RestView { + + /** + * Process the view operation by creating the resource. + * + * @param parentResource parent resource of the resource that should be created + * @param input input after parsing from request. + * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion + * to JSON. + * @throws RestApiException if the resource creation is rejected + * @throws Exception the implementation of the view failed. The exception will be logged and HTTP + * 500 Internal Server Error will be returned to the client. + */ + Object apply(P parentResource, IdString id, I input) throws Exception; +} diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java index 546bb9fd8b..1196e470f7 100644 --- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java +++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java @@ -68,7 +68,6 @@ import com.google.gerrit.common.RawInputUtil; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AcceptsDelete; import com.google.gerrit.extensions.restapi.AcceptsPost; import com.google.gerrit.extensions.restapi.AuthException; @@ -88,6 +87,7 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestCollection; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestResource; @@ -323,11 +323,15 @@ public class RestApiServlet extends HttpServlet { checkPreconditions(req); } } catch (ResourceNotFoundException e) { - if (rc instanceof AcceptsCreate && path.isEmpty() && (isPost(req) || isPut(req))) { - @SuppressWarnings("unchecked") - AcceptsCreate ac = (AcceptsCreate) rc; - viewData = new ViewData(null, ac.create(rsrc, id)); + if (!path.isEmpty() || (!isPost(req) && !isPut(req))) { + throw e; + } + + RestView createView = rc.views().get("gerrit", "CREATE./"); + if (createView != null) { + viewData = new ViewData(null, createView); status = SC_CREATED; + path.add(id); } else { throw e; } @@ -365,12 +369,20 @@ public class RestApiServlet extends HttpServlet { checkPreconditions(req); viewData = new ViewData(null, null); } catch (ResourceNotFoundException e) { - if (c instanceof AcceptsCreate && path.isEmpty() && (isPost(req) || isPut(req))) { - @SuppressWarnings("unchecked") - AcceptsCreate ac = (AcceptsCreate) c; - viewData = new ViewData(viewData.pluginName, ac.create(rsrc, id)); - status = SC_CREATED; - } else if (c instanceof AcceptsDelete && path.isEmpty() && isDelete(req)) { + if (!path.isEmpty()) { + throw e; + } + + if (isPost(req) || isPut(req)) { + RestView createView = c.views().get("gerrit", "CREATE./"); + if (createView != null) { + viewData = new ViewData(null, createView); + status = SC_CREATED; + path.add(id); + } else { + throw e; + } + } else if (c instanceof AcceptsDelete && isDelete(req)) { @SuppressWarnings("unchecked") AcceptsDelete ac = (AcceptsDelete) c; viewData = new ViewData(viewData.pluginName, ac.delete(rsrc, id)); @@ -409,6 +421,19 @@ public class RestApiServlet extends HttpServlet { ServletUtils.consumeRequestBody(is); } } + } else if (viewData.view instanceof RestCreateView) { + @SuppressWarnings("unchecked") + RestCreateView m = + (RestCreateView) viewData.view; + + Type type = inputType(m); + inputRequestBody = parseRequest(req, type); + result = m.apply(rsrc, path.get(0), inputRequestBody); + if (inputRequestBody instanceof RawInput) { + try (InputStream is = req.getInputStream()) { + ServletUtils.consumeRequestBody(is); + } + } } else { throw new ResourceNotFoundException(); } @@ -735,6 +760,24 @@ public class RestApiServlet extends HttpServlet { return ((ParameterizedType) supertype).getActualTypeArguments()[1]; } + private static Type inputType(RestCreateView m) { + // MyCreateView implements RestCreateView + TypeLiteral typeLiteral = TypeLiteral.get(m.getClass()); + + // RestCreateView + // This is smart enough to resolve even when there are intervening subclasses, even if they have + // reordered type arguments. + TypeLiteral supertypeLiteral = typeLiteral.getSupertype(RestCreateView.class); + + Type supertype = supertypeLiteral.getType(); + checkState( + supertype instanceof ParameterizedType, + "supertype of %s is not parameterized: %s", + typeLiteral, + supertypeLiteral); + return ((ParameterizedType) supertype).getActualTypeArguments()[2]; + } + private Object parseRequest(HttpServletRequest req, Type type) throws IOException, BadRequestException, SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InstantiationException, diff --git a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java index ead73c496f..2d6ef2cc88 100644 --- a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java +++ b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java @@ -111,7 +111,7 @@ public class AccountApiImpl implements AccountApi { private final Stars.Get starsGet; private final Stars.Post starsPost; private final GetEmails getEmails; - private final CreateEmail.Factory createEmailFactory; + private final CreateEmail createEmail; private final DeleteEmail deleteEmail; private final GpgApiAdapter gpgApiAdapter; private final GetSshKeys getSshKeys; @@ -151,7 +151,7 @@ public class AccountApiImpl implements AccountApi { Stars.Get starsGet, Stars.Post starsPost, GetEmails getEmails, - CreateEmail.Factory createEmailFactory, + CreateEmail createEmail, DeleteEmail deleteEmail, GpgApiAdapter gpgApiAdapter, GetSshKeys getSshKeys, @@ -190,7 +190,7 @@ public class AccountApiImpl implements AccountApi { this.starsGet = starsGet; this.starsPost = starsPost; this.getEmails = getEmails; - this.createEmailFactory = createEmailFactory; + this.createEmail = createEmail; this.deleteEmail = deleteEmail; this.getSshKeys = getSshKeys; this.addSshKey = addSshKey; @@ -341,9 +341,8 @@ public class AccountApiImpl implements AccountApi { @Override public void starChange(String changeId) throws RestApiException { try { - ChangeResource rsrc = changes.parse(TopLevelResource.INSTANCE, IdString.fromUrl(changeId)); - starredChangesCreate.setChange(rsrc); - starredChangesCreate.apply(account, new StarredChanges.EmptyInput()); + starredChangesCreate.apply( + account, IdString.fromUrl(changeId), new StarredChanges.EmptyInput()); } catch (Exception e) { throw asRestApiException("Cannot star change", e); } @@ -412,7 +411,7 @@ public class AccountApiImpl implements AccountApi { public void addEmail(EmailInput input) throws RestApiException { AccountResource.Email rsrc = new AccountResource.Email(account.getUser(), input.email); try { - createEmailFactory.create(input.email).apply(rsrc, input); + createEmail.apply(rsrc, IdString.fromDecoded(input.email), input); } catch (Exception e) { throw asRestApiException("Cannot add email", e); } @@ -432,7 +431,7 @@ public class AccountApiImpl implements AccountApi { public EmailApi createEmail(EmailInput input) throws RestApiException { AccountResource.Email rsrc = new AccountResource.Email(account.getUser(), input.email); try { - createEmailFactory.create(input.email).apply(rsrc, input); + createEmail.apply(rsrc, IdString.fromDecoded(input.email), input); return email(rsrc.getEmail()); } catch (Exception e) { throw asRestApiException("Cannot create email", e); diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java index 44b6610d77..ca5aca4c75 100644 --- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java +++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java @@ -45,7 +45,7 @@ public class AccountsImpl implements Accounts { private final AccountApiImpl.Factory api; private final PermissionBackend permissionBackend; private final Provider self; - private final CreateAccount.Factory createAccount; + private final CreateAccount createAccount; private final Provider queryAccountsProvider; @Inject @@ -54,7 +54,7 @@ public class AccountsImpl implements Accounts { AccountApiImpl.Factory api, PermissionBackend permissionBackend, Provider self, - CreateAccount.Factory createAccount, + CreateAccount createAccount, Provider queryAccountsProvider) { this.accounts = accounts; this.api = api; @@ -99,9 +99,13 @@ public class AccountsImpl implements Accounts { throw new BadRequestException("AccountInput must specify username"); } try { - CreateAccount impl = createAccount.create(in.username); - permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(impl.getClass())); - AccountInfo info = impl.apply(TopLevelResource.INSTANCE, in).value(); + permissionBackend + .currentUser() + .checkAny(GlobalPermission.fromAnnotation(createAccount.getClass())); + AccountInfo info = + createAccount + .apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.username), in) + .value(); return id(info._accountId); } catch (Exception e) { throw asRestApiException("Cannot create account " + in.username, e); diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java index 247be44bf2..bac6649928 100644 --- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java +++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java @@ -49,7 +49,7 @@ class GroupsImpl implements Groups { private final Provider listGroups; private final Provider queryGroups; private final PermissionBackend permissionBackend; - private final CreateGroup.Factory createGroup; + private final CreateGroup createGroup; private final GroupApiImpl.Factory api; @Inject @@ -60,7 +60,7 @@ class GroupsImpl implements Groups { Provider listGroups, Provider queryGroups, PermissionBackend permissionBackend, - CreateGroup.Factory createGroup, + CreateGroup createGroup, GroupApiImpl.Factory api) { this.accounts = accounts; this.groups = groups; @@ -90,9 +90,11 @@ class GroupsImpl implements Groups { throw new BadRequestException("GroupInput must specify name"); } try { - CreateGroup impl = createGroup.create(in.name); - permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(impl.getClass())); - GroupInfo info = impl.apply(TopLevelResource.INSTANCE, in); + permissionBackend + .currentUser() + .checkAny(GlobalPermission.fromAnnotation(createGroup.getClass())); + GroupInfo info = + createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.name), in); return id(info.id); } catch (Exception e) { throw asRestApiException("Cannot create group " + in.name, e); diff --git a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java index 78b34b878a..b3506fc5d7 100644 --- a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java +++ b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java @@ -46,7 +46,7 @@ public class BranchApiImpl implements BranchApi { } private final BranchesCollection branches; - private final CreateBranch.Factory createBranchFactory; + private final CreateBranch createBranch; private final DeleteBranch deleteBranch; private final FilesCollection filesCollection; private final GetBranch getBranch; @@ -58,7 +58,7 @@ public class BranchApiImpl implements BranchApi { @Inject BranchApiImpl( BranchesCollection branches, - CreateBranch.Factory createBranchFactory, + CreateBranch createBranch, DeleteBranch deleteBranch, FilesCollection filesCollection, GetBranch getBranch, @@ -67,7 +67,7 @@ public class BranchApiImpl implements BranchApi { @Assisted ProjectResource project, @Assisted String ref) { this.branches = branches; - this.createBranchFactory = createBranchFactory; + this.createBranch = createBranch; this.deleteBranch = deleteBranch; this.filesCollection = filesCollection; this.getBranch = getBranch; @@ -80,7 +80,7 @@ public class BranchApiImpl implements BranchApi { @Override public BranchApi create(BranchInput input) throws RestApiException { try { - createBranchFactory.create(ref).apply(project, input); + createBranch.apply(project, IdString.fromDecoded(ref), input); return this; } catch (Exception e) { throw asRestApiException("Cannot create branch", e); diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java index 46d9180319..1065bffde0 100644 --- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java +++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java @@ -88,7 +88,7 @@ public class ProjectApiImpl implements ProjectApi { } private final PermissionBackend permissionBackend; - private final CreateProject.Factory createProjectFactory; + private final CreateProject createProject; private final ProjectApiImpl.Factory projectApi; private final ProjectsCollection projects; private final GetDescription getDescription; @@ -122,7 +122,7 @@ public class ProjectApiImpl implements ProjectApi { @AssistedInject ProjectApiImpl( PermissionBackend permissionBackend, - CreateProject.Factory createProjectFactory, + CreateProject createProject, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, GetDescription getDescription, @@ -153,7 +153,7 @@ public class ProjectApiImpl implements ProjectApi { @Assisted ProjectResource project) { this( permissionBackend, - createProjectFactory, + createProject, projectApi, projects, getDescription, @@ -188,7 +188,7 @@ public class ProjectApiImpl implements ProjectApi { @AssistedInject ProjectApiImpl( PermissionBackend permissionBackend, - CreateProject.Factory createProjectFactory, + CreateProject createProject, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, GetDescription getDescription, @@ -219,7 +219,7 @@ public class ProjectApiImpl implements ProjectApi { @Assisted String name) { this( permissionBackend, - createProjectFactory, + createProject, projectApi, projects, getDescription, @@ -253,7 +253,7 @@ public class ProjectApiImpl implements ProjectApi { private ProjectApiImpl( PermissionBackend permissionBackend, - CreateProject.Factory createProjectFactory, + CreateProject createProject, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, GetDescription getDescription, @@ -284,7 +284,7 @@ public class ProjectApiImpl implements ProjectApi { SetParent setParent, String name) { this.permissionBackend = permissionBackend; - this.createProjectFactory = createProjectFactory; + this.createProject = createProject; this.projectApi = projectApi; this.projects = projects; this.getDescription = getDescription; @@ -330,9 +330,10 @@ public class ProjectApiImpl implements ProjectApi { if (in.name != null && !name.equals(in.name)) { throw new BadRequestException("name must match input.name"); } - CreateProject impl = createProjectFactory.create(name); - permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(impl.getClass())); - impl.apply(TopLevelResource.INSTANCE, in); + permissionBackend + .currentUser() + .checkAny(GlobalPermission.fromAnnotation(createProject.getClass())); + createProject.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(name), in); return projectApi.create(projects.parse(name)); } catch (Exception e) { throw asRestApiException("Cannot create project: " + e.getMessage(), e); diff --git a/java/com/google/gerrit/server/api/projects/TagApiImpl.java b/java/com/google/gerrit/server/api/projects/TagApiImpl.java index 03e2162297..005486a870 100644 --- a/java/com/google/gerrit/server/api/projects/TagApiImpl.java +++ b/java/com/google/gerrit/server/api/projects/TagApiImpl.java @@ -39,7 +39,7 @@ public class TagApiImpl implements TagApi { } private final ListTags listTags; - private final CreateTag.Factory createTagFactory; + private final CreateTag createTag; private final DeleteTag deleteTag; private final TagsCollection tags; private final String ref; @@ -48,13 +48,13 @@ public class TagApiImpl implements TagApi { @Inject TagApiImpl( ListTags listTags, - CreateTag.Factory createTagFactory, + CreateTag createTag, DeleteTag deleteTag, TagsCollection tags, @Assisted ProjectResource project, @Assisted String ref) { this.listTags = listTags; - this.createTagFactory = createTagFactory; + this.createTag = createTag; this.deleteTag = deleteTag; this.tags = tags; this.project = project; @@ -64,7 +64,7 @@ public class TagApiImpl implements TagApi { @Override public TagApi create(TagInput input) throws RestApiException { try { - createTagFactory.create(ref).apply(project, input); + createTag.apply(project, IdString.fromDecoded(ref), input); return this; } catch (Exception e) { throw asRestApiException("Cannot create tag", e); diff --git a/java/com/google/gerrit/server/plugins/InstallPlugin.java b/java/com/google/gerrit/server/plugins/InstallPlugin.java index ee9099eca3..f1c1fe75b4 100644 --- a/java/com/google/gerrit/server/plugins/InstallPlugin.java +++ b/java/com/google/gerrit/server/plugins/InstallPlugin.java @@ -19,8 +19,10 @@ import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.common.InstallPluginInput; import com.google.gerrit.extensions.common.PluginInfo; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.inject.Inject; @@ -91,6 +93,26 @@ public class InstallPlugin implements RestModifyView { + private final PluginLoader loader; + private final Provider install; + + @Inject + Create(PluginLoader loader, Provider install) { + this.loader = loader; + this.install = install; + } + + @Override + public Response apply( + TopLevelResource parentResource, IdString id, InstallPluginInput input) throws Exception { + loader.checkRemoteAdminEnabled(); + return install.get().setName(id.get()).setCreated(true).apply(parentResource, input); + } + } + @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) static class Overwrite implements RestModifyView { private final Provider install; diff --git a/java/com/google/gerrit/server/plugins/PluginRestApiModule.java b/java/com/google/gerrit/server/plugins/PluginRestApiModule.java index cad0e1e616..8dbea781d1 100644 --- a/java/com/google/gerrit/server/plugins/PluginRestApiModule.java +++ b/java/com/google/gerrit/server/plugins/PluginRestApiModule.java @@ -27,6 +27,7 @@ public class PluginRestApiModule extends RestApiModule { requireBinding(Key.get(PluginUser.Factory.class)); bind(PluginsCollection.class); DynamicMap.mapOf(binder(), PLUGIN_KIND); + create(PLUGIN_KIND).to(InstallPlugin.Create.class); put(PLUGIN_KIND).to(InstallPlugin.Overwrite.class); delete(PLUGIN_KIND).to(DisablePlugin.class); get(PLUGIN_KIND, "status").to(GetStatus.class); diff --git a/java/com/google/gerrit/server/plugins/PluginsCollection.java b/java/com/google/gerrit/server/plugins/PluginsCollection.java index 0d2a0188e4..7cc006e5bb 100644 --- a/java/com/google/gerrit/server/plugins/PluginsCollection.java +++ b/java/com/google/gerrit/server/plugins/PluginsCollection.java @@ -15,10 +15,8 @@ package com.google.gerrit.server.plugins; 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.RestApiException; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.TopLevelResource; @@ -27,24 +25,18 @@ import com.google.inject.Provider; import com.google.inject.Singleton; @Singleton -public class PluginsCollection - implements RestCollection, AcceptsCreate { +public class PluginsCollection implements RestCollection { private final DynamicMap> views; private final PluginLoader loader; private final Provider list; - private final Provider install; @Inject public PluginsCollection( - DynamicMap> views, - PluginLoader loader, - Provider list, - Provider install) { + DynamicMap> views, PluginLoader loader, Provider list) { this.views = views; this.loader = loader; this.list = list; - this.install = install; } @Override @@ -66,12 +58,6 @@ public class PluginsCollection return new PluginResource(p); } - @Override - public InstallPlugin create(TopLevelResource parent, IdString id) throws RestApiException { - loader.checkRemoteAdminEnabled(); - return install.get().setName(id.get()).setCreated(true); - } - @Override public DynamicMap> views() { return views; diff --git a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java index 6cec565912..370833cd31 100644 --- a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java +++ b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java @@ -16,11 +16,9 @@ package com.google.gerrit.server.restapi.account; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.TopLevelResource; @@ -40,15 +38,13 @@ import java.io.IOException; import org.eclipse.jgit.errors.ConfigInvalidException; @Singleton -public class AccountsCollection - implements RestCollection, AcceptsCreate { +public class AccountsCollection implements RestCollection { private final Provider self; private final AccountResolver resolver; private final AccountControl.Factory accountControlFactory; private final IdentifiedUser.GenericFactory userFactory; private final Provider list; private final DynamicMap> views; - private final CreateAccount.Factory createAccountFactory; @Inject public AccountsCollection( @@ -57,15 +53,13 @@ public class AccountsCollection AccountControl.Factory accountControlFactory, IdentifiedUser.GenericFactory userFactory, Provider list, - DynamicMap> views, - CreateAccount.Factory createAccountFactory) { + DynamicMap> views) { this.self = self; this.resolver = resolver; this.accountControlFactory = accountControlFactory; this.userFactory = userFactory; this.list = list; this.views = views; - this.createAccountFactory = createAccountFactory; } @Override @@ -159,9 +153,4 @@ public class AccountsCollection public DynamicMap> views() { return views; } - - @Override - public CreateAccount create(TopLevelResource parent, IdString username) throws RestApiException { - return createAccountFactory.create(username.get()); - } } diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java index 404b3d3462..b1581abbaf 100644 --- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java +++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java @@ -29,9 +29,10 @@ import com.google.gerrit.extensions.api.accounts.AccountInput; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; 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.RestCreateView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.Account; @@ -40,6 +41,7 @@ import com.google.gerrit.server.Sequences; import com.google.gerrit.server.UserInitiated; import com.google.gerrit.server.account.AccountExternalIdCreator; import com.google.gerrit.server.account.AccountLoader; +import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.AccountsUpdate; import com.google.gerrit.server.account.VersionedAuthorizedKeys; import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException; @@ -52,7 +54,6 @@ import com.google.gerrit.server.ssh.SshKeyCache; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; @@ -61,11 +62,8 @@ import java.util.Set; import org.eclipse.jgit.errors.ConfigInvalidException; @RequiresCapability(GlobalCapability.CREATE_ACCOUNT) -public class CreateAccount implements RestModifyView { - public interface Factory { - CreateAccount create(String username); - } - +public class CreateAccount + implements RestCreateView { private final Sequences seq; private final GroupsCollection groupsCollection; private final VersionedAuthorizedKeys.Accessor authorizedKeys; @@ -75,7 +73,6 @@ public class CreateAccount implements RestModifyView externalIdCreators; private final Provider groupsUpdate; private final OutgoingEmailValidator validator; - private final String username; @Inject CreateAccount( @@ -87,8 +84,7 @@ public class CreateAccount implements RestModifyView externalIdCreators, @UserInitiated Provider groupsUpdate, - OutgoingEmailValidator validator, - @Assisted String username) { + OutgoingEmailValidator validator) { this.seq = seq; this.groupsCollection = groupsCollection; this.authorizedKeys = authorizedKeys; @@ -98,23 +94,23 @@ public class CreateAccount implements RestModifyView apply(TopLevelResource rsrc, @Nullable AccountInput input) + public Response apply( + TopLevelResource rsrc, IdString id, @Nullable AccountInput input) throws BadRequestException, ResourceConflictException, UnprocessableEntityException, OrmException, IOException, ConfigInvalidException { - return apply(input != null ? input : new AccountInput()); + return apply(id, input != null ? input : new AccountInput()); } - public Response apply(AccountInput input) + public Response apply(IdString id, AccountInput input) throws BadRequestException, ResourceConflictException, UnprocessableEntityException, OrmException, IOException, ConfigInvalidException { + String username = id.get(); if (input.username != null && !username.equals(input.username)) { throw new BadRequestException("username must match URL"); } - if (!ExternalId.isValidUsername(username)) { throw new BadRequestException( "Username '" + username + "' must contain only letters, numbers, _, - or ."); @@ -122,19 +118,19 @@ public class CreateAccount implements RestModifyView groups = parseGroups(input.groups); - Account.Id id = new Account.Id(seq.nextAccountId()); + Account.Id accountId = new Account.Id(seq.nextAccountId()); List extIds = new ArrayList<>(); if (input.email != null) { if (!validator.isValid(input.email)) { throw new BadRequestException("invalid email address"); } - extIds.add(ExternalId.createEmail(id, input.email)); + extIds.add(ExternalId.createEmail(accountId, input.email)); } - extIds.add(ExternalId.createUsername(username, id, input.httpPassword)); + extIds.add(ExternalId.createUsername(username, accountId, input.httpPassword)); for (AccountExternalIdCreator c : externalIdCreators) { - extIds.addAll(c.create(id, username, input.email)); + extIds.addAll(c.create(accountId, username, input.email)); } try { @@ -142,7 +138,7 @@ public class CreateAccount implements RestModifyView u.setFullName(input.name).setPreferredEmail(input.email).addExternalIds(extIds)); } catch (DuplicateExternalIdKeyException e) { if (e.getDuplicateKey().isScheme(SCHEME_USERNAME)) { @@ -159,7 +155,7 @@ public class CreateAccount implements RestModifyView { +public class CreateEmail + implements RestCreateView { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - public interface Factory { - CreateEmail create(String email); - } - private final Provider self; private final Realm realm; private final PermissionBackend permissionBackend; @@ -61,7 +58,6 @@ public class CreateEmail implements RestModifyView private final RegisterNewEmailSender.Factory registerNewEmailFactory; private final PutPreferred putPreferred; private final OutgoingEmailValidator validator; - private final String email; private final boolean isDevMode; @Inject @@ -73,8 +69,7 @@ public class CreateEmail implements RestModifyView AccountManager accountManager, RegisterNewEmailSender.Factory registerNewEmailFactory, PutPreferred putPreferred, - OutgoingEmailValidator validator, - @Assisted String email) { + OutgoingEmailValidator validator) { this.self = self; this.realm = realm; this.permissionBackend = permissionBackend; @@ -82,12 +77,11 @@ public class CreateEmail implements RestModifyView this.registerNewEmailFactory = registerNewEmailFactory; this.putPreferred = putPreferred; this.validator = validator; - this.email = email != null ? email.trim() : null; this.isDevMode = authConfig.getAuthType() == DEVELOPMENT_BECOME_ANY_ACCOUNT; } @Override - public Response apply(AccountResource rsrc, EmailInput input) + public Response apply(AccountResource rsrc, IdString id, EmailInput input) throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException, ConfigInvalidException, PermissionBackendException { if (input == null) { @@ -102,13 +96,15 @@ public class CreateEmail implements RestModifyView throw new MethodNotAllowedException("realm does not allow adding emails"); } - return apply(rsrc.getUser(), input); + return apply(rsrc.getUser(), id, input); } /** To be used from plugins that want to create emails without permission checks. */ - public Response apply(IdentifiedUser user, EmailInput input) + public Response apply(IdentifiedUser user, IdString id, EmailInput input) throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException, ConfigInvalidException, PermissionBackendException { + String email = id.get().trim(); + if (input == null) { input = new EmailInput(); } diff --git a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java index 8694da04b6..434b9d6174 100644 --- a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java +++ b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java @@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.account; import com.google.common.base.Strings; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; @@ -33,27 +32,22 @@ import com.google.inject.Provider; import com.google.inject.Singleton; @Singleton -public class EmailsCollection - implements ChildCollection, - AcceptsCreate { +public class EmailsCollection implements ChildCollection { private final DynamicMap> views; private final GetEmails list; private final Provider self; private final PermissionBackend permissionBackend; - private final CreateEmail.Factory createEmailFactory; @Inject EmailsCollection( DynamicMap> views, GetEmails list, Provider self, - PermissionBackend permissionBackend, - CreateEmail.Factory createEmailFactory) { + PermissionBackend permissionBackend) { this.views = views; this.list = list; this.self = self; this.permissionBackend = permissionBackend; - this.createEmailFactory = createEmailFactory; } @Override @@ -85,9 +79,4 @@ public class EmailsCollection public DynamicMap> views() { return views; } - - @Override - public CreateEmail create(AccountResource parent, IdString email) { - return createEmailFactory.create(email.get()); - } } diff --git a/java/com/google/gerrit/server/restapi/account/Module.java b/java/com/google/gerrit/server/restapi/account/Module.java index ca5f08e434..7b09bc9cae 100644 --- a/java/com/google/gerrit/server/restapi/account/Module.java +++ b/java/com/google/gerrit/server/restapi/account/Module.java @@ -43,6 +43,7 @@ public class Module extends RestApiModule { DynamicMap.mapOf(binder(), STARRED_CHANGE_KIND); DynamicMap.mapOf(binder(), STAR_KIND); + create(ACCOUNT_KIND).to(CreateAccount.class); put(ACCOUNT_KIND).to(PutAccount.class); get(ACCOUNT_KIND).to(GetAccount.class); get(ACCOUNT_KIND, "detail").to(GetDetail.class); @@ -58,6 +59,7 @@ public class Module extends RestApiModule { put(ACCOUNT_KIND, "active").to(PutActive.class); delete(ACCOUNT_KIND, "active").to(DeleteActive.class); child(ACCOUNT_KIND, "emails").to(EmailsCollection.class); + create(EMAIL_KIND).to(CreateEmail.class); get(EMAIL_KIND).to(GetEmail.class); put(EMAIL_KIND).to(PutEmail.class); delete(EMAIL_KIND).to(DeleteEmail.class); @@ -93,6 +95,7 @@ public class Module extends RestApiModule { put(ACCOUNT_KIND, "agreements").to(PutAgreement.class); child(ACCOUNT_KIND, "starred.changes").to(StarredChanges.class); + create(STARRED_CHANGE_KIND).to(StarredChanges.Create.class); put(STARRED_CHANGE_KIND).to(StarredChanges.Put.class); delete(STARRED_CHANGE_KIND).to(StarredChanges.Delete.class); bind(StarredChanges.Create.class); @@ -104,8 +107,6 @@ public class Module extends RestApiModule { get(ACCOUNT_KIND, "external.ids").to(GetExternalIds.class); post(ACCOUNT_KIND, "external.ids:delete").to(DeleteExternalIds.class); - factory(CreateAccount.Factory.class); - factory(CreateEmail.Factory.class); factory(AccountsUpdate.Factory.class); } diff --git a/java/com/google/gerrit/server/restapi/account/StarredChanges.java b/java/com/google/gerrit/server/restapi/account/StarredChanges.java index e804b64a6d..e049539855 100644 --- a/java/com/google/gerrit/server/restapi/account/StarredChanges.java +++ b/java/com/google/gerrit/server/restapi/account/StarredChanges.java @@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.account; import com.google.common.flogger.FluentLogger; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.ChildCollection; @@ -25,6 +24,7 @@ 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.RestApiException; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestView; @@ -49,24 +49,20 @@ import java.io.IOException; @Singleton public class StarredChanges - implements ChildCollection, - AcceptsCreate { + implements ChildCollection { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final ChangesCollection changes; private final DynamicMap> views; - private final Provider createProvider; private final StarredChangesUtil starredChangesUtil; @Inject StarredChanges( ChangesCollection changes, DynamicMap> views, - Provider createProvider, StarredChangesUtil starredChangesUtil) { this.changes = changes; this.views = views; - this.createProvider = createProvider; this.starredChangesUtil = starredChangesUtil; } @@ -101,42 +97,40 @@ public class StarredChanges }; } - @Override - public RestModifyView create(AccountResource parent, IdString id) - throws RestApiException { - try { - return createProvider.get().setChange(changes.parse(TopLevelResource.INSTANCE, id)); - } catch (ResourceNotFoundException e) { - throw new UnprocessableEntityException(String.format("change %s not found", id.get())); - } catch (OrmException | PermissionBackendException | IOException e) { - logger.atSevere().withCause(e).log("cannot resolve change"); - throw new UnprocessableEntityException("internal server error"); - } - } - @Singleton - public static class Create implements RestModifyView { + public static class Create + implements RestCreateView { private final Provider self; + private final ChangesCollection changes; private final StarredChangesUtil starredChangesUtil; - private ChangeResource change; @Inject - Create(Provider self, StarredChangesUtil starredChangesUtil) { + Create( + Provider self, + ChangesCollection changes, + StarredChangesUtil starredChangesUtil) { this.self = self; + this.changes = changes; this.starredChangesUtil = starredChangesUtil; } - public Create setChange(ChangeResource change) { - this.change = change; - return this; - } - @Override - public Response apply(AccountResource rsrc, EmptyInput in) + public Response apply(AccountResource rsrc, IdString id, EmptyInput in) throws RestApiException, OrmException, IOException { if (!self.get().hasSameAccountId(rsrc.getUser())) { throw new AuthException("not allowed to add starred change"); } + + ChangeResource change; + try { + change = changes.parse(TopLevelResource.INSTANCE, id); + } catch (ResourceNotFoundException e) { + throw new UnprocessableEntityException(String.format("change %s not found", id.get())); + } catch (OrmException | PermissionBackendException | IOException e) { + logger.atSevere().withCause(e).log("cannot resolve change"); + throw new UnprocessableEntityException("internal server error"); + } + try { starredChangesUtil.star( self.get().getAccountId(), diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java index b7a029b89d..1e24a7f523 100644 --- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java +++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java @@ -19,7 +19,6 @@ import com.google.gerrit.extensions.common.DiffWebLinkInfo; import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.Input; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AcceptsDelete; import com.google.gerrit.extensions.restapi.AcceptsPost; import com.google.gerrit.extensions.restapi.AuthException; @@ -33,6 +32,7 @@ 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.RestApiException; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestView; @@ -72,11 +72,9 @@ import org.kohsuke.args4j.Option; @Singleton public class ChangeEdits implements ChildCollection, - AcceptsCreate, AcceptsPost, AcceptsDelete { private final DynamicMap> views; - private final Create.Factory createFactory; private final DeleteFile.Factory deleteFileFactory; private final Provider detail; private final ChangeEditUtil editUtil; @@ -85,13 +83,11 @@ public class ChangeEdits @Inject ChangeEdits( DynamicMap> views, - Create.Factory createFactory, Provider detail, ChangeEditUtil editUtil, Post post, DeleteFile.Factory deleteFileFactory) { this.views = views; - this.createFactory = createFactory; this.detail = detail; this.editUtil = editUtil; this.post = post; @@ -118,11 +114,6 @@ public class ChangeEdits return new ChangeEditResource(rsrc, edit.get(), id.get()); } - @Override - public Create create(ChangeResource parent, IdString id) throws RestApiException { - return createFactory.create(id.get()); - } - @Override public Post post(ChangeResource parent) throws RestApiException { return post; @@ -141,26 +132,20 @@ public class ChangeEdits return deleteFileFactory.create(id.get()); } - public static class Create implements RestModifyView { - - interface Factory { - Create create(String path); - } - + public static class Create + implements RestCreateView { private final Put putEdit; - private final String path; @Inject - Create(Put putEdit, @Assisted String path) { + Create(Put putEdit) { this.putEdit = putEdit; - this.path = path; } @Override - public Response apply(ChangeResource resource, Put.Input input) + public Response apply(ChangeResource resource, IdString id, Put.Input input) throws AuthException, ResourceConflictException, IOException, OrmException, PermissionBackendException { - putEdit.apply(resource, path, input.content); + putEdit.apply(resource, id.get(), input.content); return Response.none(); } } diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java index 7955fa59d6..d27b136ef1 100644 --- a/java/com/google/gerrit/server/restapi/change/Module.java +++ b/java/com/google/gerrit/server/restapi/change/Module.java @@ -165,6 +165,7 @@ public class Module extends RestApiModule { get(FILE_KIND, "blame").to(GetBlame.class); child(CHANGE_KIND, "edit").to(ChangeEdits.class); + create(CHANGE_EDIT_KIND).to(ChangeEdits.Create.class); delete(CHANGE_KIND, "edit").to(DeleteChangeEdit.class); child(CHANGE_KIND, "edit:publish").to(PublishChangeEdit.class); child(CHANGE_KIND, "edit:rebase").to(RebaseChangeEdit.class); @@ -180,7 +181,6 @@ public class Module extends RestApiModule { get(CHANGE_MESSAGE_KIND).to(GetChangeMessage.class); factory(AccountLoader.Factory.class); - factory(ChangeEdits.Create.Factory.class); factory(ChangeEdits.DeleteFile.Factory.class); factory(ChangeInserter.Factory.class); factory(ChangeResource.Factory.class); diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java index 84fbbb7d3f..3e2d1e7de1 100644 --- a/java/com/google/gerrit/server/restapi/group/AddMembers.java +++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java @@ -23,8 +23,10 @@ import com.google.gerrit.extensions.client.AuthType; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.Account; @@ -211,22 +213,20 @@ public class AddMembers implements RestModifyView { return result; } - public static class PutMember implements RestModifyView { - + public static class CreateMember implements RestCreateView { private final AddMembers put; - private final String id; - public PutMember(AddMembers put, String id) { + @Inject + public CreateMember(AddMembers put) { this.put = put; - this.id = id; } @Override - public AccountInfo apply(GroupResource resource, Input input) + public AccountInfo apply(GroupResource resource, IdString id, Input input) throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException, ConfigInvalidException { AddMembers.Input in = new AddMembers.Input(); - in._oneMember = id; + in._oneMember = id.get(); try { List list = put.apply(resource, in); if (list.size() == 1) { diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java index d0be5ac61a..21b698192a 100644 --- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java +++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java @@ -23,8 +23,10 @@ import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -127,22 +129,21 @@ public class AddSubgroups implements RestModifyView { groupsUpdateProvider.get().updateGroup(parentGroupUuid, groupUpdate); } - public static class PutSubgroup implements RestModifyView { - + public static class CreateSubgroup + implements RestCreateView { private final AddSubgroups addSubgroups; - private final String id; - public PutSubgroup(AddSubgroups addSubgroups, String id) { + @Inject + public CreateSubgroup(AddSubgroups addSubgroups) { this.addSubgroups = addSubgroups; - this.id = id; } @Override - public GroupInfo apply(GroupResource resource, Input input) + public GroupInfo apply(GroupResource resource, IdString id, Input input) throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException, ConfigInvalidException { AddSubgroups.Input in = new AddSubgroups.Input(); - in.groups = ImmutableList.of(id); + in.groups = ImmutableList.of(id.get()); try { List list = addSubgroups.apply(resource, in); if (list.size() == 1) { diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java index 79f9688699..6ecb5aa499 100644 --- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java +++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java @@ -26,9 +26,10 @@ import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.extensions.restapi.Url; @@ -42,6 +43,7 @@ import com.google.gerrit.server.account.CreateGroupArgs; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupUUID; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.group.GroupResource; import com.google.gerrit.server.group.InternalGroup; import com.google.gerrit.server.group.InternalGroupDescription; import com.google.gerrit.server.group.SystemGroupBackend; @@ -54,7 +56,6 @@ import com.google.gwtorm.server.OrmDuplicateKeyException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -67,11 +68,7 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.PersonIdent; @RequiresCapability(GlobalCapability.CREATE_GROUP) -public class CreateGroup implements RestModifyView { - public interface Factory { - CreateGroup create(@Assisted String name); - } - +public class CreateGroup implements RestCreateView { private final Provider self; private final PersonIdent serverIdent; private final Provider groupsUpdateProvider; @@ -82,7 +79,6 @@ public class CreateGroup implements RestModifyView private final AddMembers addMembers; private final SystemGroupBackend systemGroupBackend; private final boolean defaultVisibleToAll; - private final String name; private final Sequences sequences; @Inject @@ -97,7 +93,6 @@ public class CreateGroup implements RestModifyView AddMembers addMembers, SystemGroupBackend systemGroupBackend, @GerritServerConfig Config cfg, - @Assisted String name, Sequences sequences) { this.self = self; this.serverIdent = serverIdent; @@ -109,7 +104,6 @@ public class CreateGroup implements RestModifyView this.addMembers = addMembers; this.systemGroupBackend = systemGroupBackend; this.defaultVisibleToAll = cfg.getBoolean("groups", "newGroupsVisibleToAll", false); - this.name = name; this.sequences = sequences; } @@ -124,10 +118,11 @@ public class CreateGroup implements RestModifyView } @Override - public GroupInfo apply(TopLevelResource resource, GroupInput input) + public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input) throws AuthException, BadRequestException, UnprocessableEntityException, ResourceConflictException, OrmException, IOException, ConfigInvalidException, ResourceNotFoundException { + String name = id.get(); if (input == null) { input = new GroupInput(); } diff --git a/java/com/google/gerrit/server/restapi/group/GroupsCollection.java b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java index fba1f1fbf4..40a11c7e38 100644 --- a/java/com/google/gerrit/server/restapi/group/GroupsCollection.java +++ b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java @@ -18,13 +18,11 @@ import com.google.common.collect.ListMultimap; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.NeedsParams; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.TopLevelResource; @@ -44,13 +42,10 @@ import com.google.inject.Provider; import java.util.Optional; public class GroupsCollection - implements RestCollection, - AcceptsCreate, - NeedsParams { + implements RestCollection, NeedsParams { private final DynamicMap> views; private final Provider list; private final Provider queryGroups; - private final CreateGroup.Factory createGroup; private final GroupControl.Factory groupControlFactory; private final GroupBackend groupBackend; private final GroupCache groupCache; @@ -63,7 +58,6 @@ public class GroupsCollection DynamicMap> views, Provider list, Provider queryGroups, - CreateGroup.Factory createGroup, GroupControl.Factory groupControlFactory, GroupBackend groupBackend, GroupCache groupCache, @@ -71,7 +65,6 @@ public class GroupsCollection this.views = views; this.list = list; this.queryGroups = queryGroups; - this.createGroup = createGroup; this.groupControlFactory = groupControlFactory; this.groupBackend = groupBackend; this.groupCache = groupCache; @@ -199,11 +192,6 @@ public class GroupsCollection return null; } - @Override - public CreateGroup create(TopLevelResource root, IdString name) throws RestApiException { - return createGroup.create(name.get()); - } - @Override public DynamicMap> views() { return views; diff --git a/java/com/google/gerrit/server/restapi/group/MembersCollection.java b/java/com/google/gerrit/server/restapi/group/MembersCollection.java index cf2e0b8338..fec14436fe 100644 --- a/java/com/google/gerrit/server/restapi/group/MembersCollection.java +++ b/java/com/google/gerrit/server/restapi/group/MembersCollection.java @@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.group; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; @@ -27,7 +26,6 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.group.GroupResource; import com.google.gerrit.server.group.MemberResource; import com.google.gerrit.server.restapi.account.AccountsCollection; -import com.google.gerrit.server.restapi.group.AddMembers.PutMember; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -36,23 +34,19 @@ import java.io.IOException; import org.eclipse.jgit.errors.ConfigInvalidException; @Singleton -public class MembersCollection - implements ChildCollection, AcceptsCreate { +public class MembersCollection implements ChildCollection { private final DynamicMap> views; private final Provider list; private final AccountsCollection accounts; - private final AddMembers put; @Inject MembersCollection( DynamicMap> views, Provider list, - AccountsCollection accounts, - AddMembers put) { + AccountsCollection accounts) { this.views = views; this.list = list; this.accounts = accounts; - this.put = put; } @Override @@ -78,11 +72,6 @@ public class MembersCollection return group.getMembers().contains(user.getAccountId()); } - @Override - public PutMember create(GroupResource group, IdString id) { - return new PutMember(put, id.get()); - } - @Override public DynamicMap> views() { return views; diff --git a/java/com/google/gerrit/server/restapi/group/Module.java b/java/com/google/gerrit/server/restapi/group/Module.java index fa1e5c7796..741c3da774 100644 --- a/java/com/google/gerrit/server/restapi/group/Module.java +++ b/java/com/google/gerrit/server/restapi/group/Module.java @@ -24,7 +24,9 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.ServerInitiated; import com.google.gerrit.server.UserInitiated; import com.google.gerrit.server.group.db.GroupsUpdate; +import com.google.gerrit.server.restapi.group.AddMembers.CreateMember; import com.google.gerrit.server.restapi.group.AddMembers.UpdateMember; +import com.google.gerrit.server.restapi.group.AddSubgroups.CreateSubgroup; import com.google.gerrit.server.restapi.group.AddSubgroups.UpdateSubgroup; import com.google.gerrit.server.restapi.group.DeleteMembers.DeleteMember; import com.google.gerrit.server.restapi.group.DeleteSubgroups.DeleteSubgroup; @@ -40,6 +42,7 @@ public class Module extends RestApiModule { DynamicMap.mapOf(binder(), MEMBER_KIND); DynamicMap.mapOf(binder(), SUBGROUP_KIND); + create(GROUP_KIND).to(CreateGroup.class); get(GROUP_KIND).to(GetGroup.class); put(GROUP_KIND).to(PutGroup.class); get(GROUP_KIND, "detail").to(GetDetail.class); @@ -62,16 +65,17 @@ public class Module extends RestApiModule { get(GROUP_KIND, "log.audit").to(GetAuditLog.class); child(GROUP_KIND, "members").to(MembersCollection.class); + create(MEMBER_KIND).to(CreateMember.class); get(MEMBER_KIND).to(GetMember.class); put(MEMBER_KIND).to(UpdateMember.class); delete(MEMBER_KIND).to(DeleteMember.class); child(GROUP_KIND, "groups").to(SubgroupsCollection.class); + create(SUBGROUP_KIND).to(CreateSubgroup.class); get(SUBGROUP_KIND).to(GetSubgroup.class); put(SUBGROUP_KIND).to(UpdateSubgroup.class); delete(SUBGROUP_KIND).to(DeleteSubgroup.class); - factory(CreateGroup.Factory.class); factory(GroupsUpdate.Factory.class); } diff --git a/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java index 83520f17a7..cebc27a407 100644 --- a/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java +++ b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java @@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.group; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; @@ -25,28 +24,23 @@ import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.server.group.GroupResource; import com.google.gerrit.server.group.SubgroupResource; -import com.google.gerrit.server.restapi.group.AddSubgroups.PutSubgroup; import com.google.inject.Inject; import com.google.inject.Singleton; @Singleton -public class SubgroupsCollection - implements ChildCollection, AcceptsCreate { +public class SubgroupsCollection implements ChildCollection { private final DynamicMap> views; private final ListSubgroups list; private final GroupsCollection groupsCollection; - private final AddSubgroups addSubgroups; @Inject SubgroupsCollection( DynamicMap> views, ListSubgroups list, - GroupsCollection groupsCollection, - AddSubgroups addSubgroups) { + GroupsCollection groupsCollection) { this.views = views; this.list = list; this.groupsCollection = groupsCollection; - this.addSubgroups = addSubgroups; } @Override @@ -73,11 +67,6 @@ public class SubgroupsCollection return parent.getSubgroups().contains(member.getGroupUUID()); } - @Override - public PutSubgroup create(GroupResource group, IdString id) { - return new PutSubgroup(addSubgroups, id.get()); - } - @Override public DynamicMap> views() { return views; diff --git a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java index f8ff7b9f8b..389cc2f1a2 100644 --- a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java +++ b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java @@ -15,7 +15,6 @@ package com.google.gerrit.server.restapi.project; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; @@ -39,26 +38,22 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; @Singleton -public class BranchesCollection - implements ChildCollection, AcceptsCreate { +public class BranchesCollection implements ChildCollection { private final DynamicMap> views; private final Provider list; private final PermissionBackend permissionBackend; private final GitRepositoryManager repoManager; - private final CreateBranch.Factory createBranchFactory; @Inject BranchesCollection( DynamicMap> views, Provider list, PermissionBackend permissionBackend, - GitRepositoryManager repoManager, - CreateBranch.Factory createBranchFactory) { + GitRepositoryManager repoManager) { this.views = views; this.list = list; this.permissionBackend = permissionBackend; this.repoManager = repoManager; - this.createBranchFactory = createBranchFactory; } @Override @@ -98,9 +93,4 @@ public class BranchesCollection public DynamicMap> views() { return views; } - - @Override - public CreateBranch create(ProjectResource parent, IdString name) { - return createBranchFactory.create(name.get()); - } } diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java index 0296c9c4e6..4c7b27a06c 100644 --- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java +++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java @@ -21,8 +21,9 @@ import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.api.projects.BranchInput; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; -import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.IdentifiedUser; @@ -31,6 +32,7 @@ import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.RefPermission; +import com.google.gerrit.server.project.BranchResource; import com.google.gerrit.server.project.CreateRefControl; import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.ProjectResource; @@ -39,7 +41,6 @@ import com.google.gerrit.server.project.RefValidationHelper; import com.google.gerrit.server.util.MagicBranch; import com.google.inject.Inject; import com.google.inject.Provider; -import com.google.inject.assistedinject.Assisted; import java.io.IOException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.lib.Constants; @@ -50,20 +51,15 @@ import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.ReceiveCommand; -public class CreateBranch implements RestModifyView { +public class CreateBranch implements RestCreateView { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - public interface Factory { - CreateBranch create(String ref); - } - private final Provider identifiedUser; private final PermissionBackend permissionBackend; private final GitRepositoryManager repoManager; private final GitReferenceUpdated referenceUpdated; private final RefValidationHelper refCreationValidator; private final CreateRefControl createRefControl; - private String ref; @Inject CreateBranch( @@ -72,21 +68,20 @@ public class CreateBranch implements RestModifyView { + private final Provider setDefault; + + @Option(name = "--inherited", usage = "set dashboard inherited by children") + private boolean inherited; + + @Inject + CreateDashboard(Provider setDefault) { + this.setDefault = setDefault; + } + + @Override + public Response apply(ProjectResource parent, IdString id, SetDashboardInput input) + throws RestApiException, IOException, PermissionBackendException { + parent.getProjectState().checkStatePermitsWrite(); + if (!DashboardsCollection.isDefaultDashboard(id)) { + throw new ResourceNotFoundException(id); + } + SetDefaultDashboard set = setDefault.get(); + set.inherited = inherited; + return set.apply( + DashboardResource.projectDefault(parent.getProjectState(), parent.getUser()), input); + } +} diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java index 3a9a0e7659..09fd91442f 100644 --- a/java/com/google/gerrit/server/restapi/project/CreateProject.java +++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java @@ -37,10 +37,11 @@ import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.BooleanProjectConfig; @@ -64,13 +65,13 @@ import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.server.project.ProjectJson; import com.google.gerrit.server.project.ProjectNameLockManager; +import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.restapi.group.GroupsCollection; 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; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -89,13 +90,10 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.ReceiveCommand; @RequiresCapability(GlobalCapability.CREATE_PROJECT) -public class CreateProject implements RestModifyView { +public class CreateProject + implements RestCreateView { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - public interface Factory { - CreateProject create(String name); - } - private final Provider projectsCollection; private final Provider groupsCollection; private final DynamicSet projectCreationValidationListeners; @@ -114,7 +112,6 @@ public class CreateProject implements RestModifyView lockManager; - private final String name; @Inject CreateProject( @@ -135,8 +132,7 @@ public class CreateProject implements RestModifyView putConfig, AllProjectsName allProjects, AllUsersName allUsers, - DynamicItem lockManager, - @Assisted String name) { + DynamicItem lockManager) { this.projectsCollection = projectsCollection; this.groupsCollection = groupsCollection; this.projectCreationValidationListeners = projectCreationValidationListeners; @@ -155,12 +151,12 @@ public class CreateProject implements RestModifyView apply(TopLevelResource resource, ProjectInput input) + public Response apply(TopLevelResource resource, IdString id, ProjectInput input) throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException { + String name = id.get(); if (input == null) { input = new ProjectInput(); } diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java index b09d8706b4..b2fdff28be 100644 --- a/java/com/google/gerrit/server/restapi/project/CreateTag.java +++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java @@ -23,10 +23,11 @@ import com.google.gerrit.extensions.api.projects.TagInfo; import com.google.gerrit.extensions.api.projects.TagInput; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestCreateView; import com.google.gerrit.server.WebLinks; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.GitRepositoryManager; @@ -38,8 +39,8 @@ import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.RefUtil; import com.google.gerrit.server.project.RefUtil.InvalidRevisionException; +import com.google.gerrit.server.project.TagResource; import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; import java.io.IOException; import java.util.TimeZone; import org.eclipse.jgit.api.Git; @@ -52,19 +53,13 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; -public class CreateTag implements RestModifyView { +public class CreateTag implements RestCreateView { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - public interface Factory { - CreateTag create(String ref); - } - private final PermissionBackend permissionBackend; private final GitRepositoryManager repoManager; private final TagCache tagCache; private final GitReferenceUpdated referenceUpdated; private final WebLinks links; - private String ref; @Inject CreateTag( @@ -72,19 +67,18 @@ public class CreateTag implements RestModifyView { GitRepositoryManager repoManager, TagCache tagCache, GitReferenceUpdated referenceUpdated, - WebLinks webLinks, - @Assisted String ref) { + WebLinks webLinks) { this.permissionBackend = permissionBackend; this.repoManager = repoManager; this.tagCache = tagCache; this.referenceUpdated = referenceUpdated; this.links = webLinks; - this.ref = ref; } @Override - public TagInfo apply(ProjectResource resource, TagInput input) + public TagInfo apply(ProjectResource resource, IdString id, TagInput input) throws RestApiException, IOException, PermissionBackendException, NoSuchProjectException { + String ref = id.get(); if (input == null) { input = new TagInput(); } diff --git a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java index b7589cfe0a..07691e7251 100644 --- a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java +++ b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java @@ -25,14 +25,12 @@ import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.api.projects.DashboardInfo; import com.google.gerrit.extensions.api.projects.DashboardSectionInfo; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.Project; @@ -60,14 +58,12 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; @Singleton -public class DashboardsCollection - implements ChildCollection, AcceptsCreate { +public class DashboardsCollection implements ChildCollection { public static final String DEFAULT_DASHBOARD_NAME = "default"; private final GitRepositoryManager gitManager; private final DynamicMap> views; private final Provider list; - private final Provider createDefault; private final PermissionBackend permissionBackend; @Inject @@ -75,12 +71,10 @@ public class DashboardsCollection GitRepositoryManager gitManager, DynamicMap> views, Provider list, - Provider createDefault, PermissionBackend permissionBackend) { this.gitManager = gitManager; this.views = views; this.list = list; - this.createDefault = createDefault; this.permissionBackend = permissionBackend; } @@ -97,16 +91,6 @@ public class DashboardsCollection return list.get(); } - @Override - public RestModifyView create(ProjectResource parent, IdString id) - throws RestApiException { - parent.getProjectState().checkStatePermitsWrite(); - if (isDefaultDashboard(id)) { - return createDefault.get(); - } - throw new ResourceNotFoundException(id); - } - @Override public DashboardResource parse(ProjectResource parent, IdString id) throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException { diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java index 17a675d618..d2e59cd783 100644 --- a/java/com/google/gerrit/server/restapi/project/Module.java +++ b/java/com/google/gerrit/server/restapi/project/Module.java @@ -41,6 +41,7 @@ public class Module extends RestApiModule { DynamicMap.mapOf(binder(), COMMIT_KIND); DynamicMap.mapOf(binder(), TAG_KIND); + create(PROJECT_KIND).to(CreateProject.class); put(PROJECT_KIND).to(PutProject.class); get(PROJECT_KIND).to(GetProject.class); get(PROJECT_KIND, "description").to(GetDescription.class); @@ -69,11 +70,11 @@ public class Module extends RestApiModule { post(PROJECT_KIND, "index").to(Index.class); child(PROJECT_KIND, "branches").to(BranchesCollection.class); + create(BRANCH_KIND).to(CreateBranch.class); put(BRANCH_KIND).to(PutBranch.class); get(BRANCH_KIND).to(GetBranch.class); delete(BRANCH_KIND).to(DeleteBranch.class); post(PROJECT_KIND, "branches:delete").to(DeleteBranches.class); - factory(CreateBranch.Factory.class); get(BRANCH_KIND, "mergeable").to(CheckMergeability.class); factory(RefValidationHelper.Factory.class); get(BRANCH_KIND, "reflog").to(GetReflog.class); @@ -86,17 +87,17 @@ public class Module extends RestApiModule { child(COMMIT_KIND, "files").to(FilesInCommitCollection.class); child(PROJECT_KIND, "tags").to(TagsCollection.class); + create(TAG_KIND).to(CreateTag.class); get(TAG_KIND).to(GetTag.class); put(TAG_KIND).to(PutTag.class); delete(TAG_KIND).to(DeleteTag.class); post(PROJECT_KIND, "tags:delete").to(DeleteTags.class); - factory(CreateTag.Factory.class); child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class); + create(DASHBOARD_KIND).to(CreateDashboard.class); get(DASHBOARD_KIND).to(GetDashboard.class); put(DASHBOARD_KIND).to(SetDashboard.class); delete(DASHBOARD_KIND).to(DeleteDashboard.class); - factory(CreateProject.Factory.class); get(PROJECT_KIND, "config").to(GetConfig.class); put(PROJECT_KIND, "config").to(PutConfig.class); diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java index 1ba993cce5..dafc2fab6f 100644 --- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java +++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java @@ -17,7 +17,6 @@ package com.google.gerrit.server.restapi.project; import com.google.common.collect.ListMultimap; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; @@ -46,16 +45,13 @@ import org.eclipse.jgit.lib.Constants; @Singleton public class ProjectsCollection - implements RestCollection, - AcceptsCreate, - NeedsParams { + implements RestCollection, NeedsParams { private final DynamicMap> views; private final Provider list; private final Provider queryProjects; private final ProjectCache projectCache; private final PermissionBackend permissionBackend; private final Provider user; - private final CreateProject.Factory createProjectFactory; private boolean hasQuery; @@ -66,7 +62,6 @@ public class ProjectsCollection Provider queryProjects, ProjectCache projectCache, PermissionBackend permissionBackend, - CreateProject.Factory factory, Provider user) { this.views = views; this.list = list; @@ -74,7 +69,6 @@ public class ProjectsCollection this.projectCache = projectCache; this.permissionBackend = permissionBackend; this.user = user; - this.createProjectFactory = factory; } @Override @@ -179,9 +173,4 @@ public class ProjectsCollection public DynamicMap> views() { return views; } - - @Override - public CreateProject create(TopLevelResource parent, IdString name) throws RestApiException { - return createProjectFactory.create(name.get()); - } } diff --git a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java index ba91e0ef3b..68fef53425 100644 --- a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java +++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java @@ -49,7 +49,7 @@ class SetDefaultDashboard implements RestModifyView { - private final Provider setDefault; - - @Option(name = "--inherited", usage = "set dashboard inherited by children") - private boolean inherited; - - @Inject - CreateDefault(Provider setDefault) { - this.setDefault = setDefault; - } - - @Override - public Response apply(ProjectResource resource, SetDashboardInput input) - throws RestApiException, IOException, PermissionBackendException { - SetDefaultDashboard set = setDefault.get(); - set.inherited = inherited; - return set.apply( - DashboardResource.projectDefault(resource.getProjectState(), resource.getUser()), input); - } - } } diff --git a/java/com/google/gerrit/server/restapi/project/TagsCollection.java b/java/com/google/gerrit/server/restapi/project/TagsCollection.java index fdace77afd..a129bda834 100644 --- a/java/com/google/gerrit/server/restapi/project/TagsCollection.java +++ b/java/com/google/gerrit/server/restapi/project/TagsCollection.java @@ -15,7 +15,6 @@ package com.google.gerrit.server.restapi.project; import com.google.gerrit.extensions.registration.DynamicMap; -import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; @@ -30,20 +29,14 @@ import com.google.inject.Singleton; import java.io.IOException; @Singleton -public class TagsCollection - implements ChildCollection, AcceptsCreate { +public class TagsCollection implements ChildCollection { private final DynamicMap> views; private final Provider list; - private final CreateTag.Factory createTagFactory; @Inject - public TagsCollection( - DynamicMap> views, - Provider list, - CreateTag.Factory createTagFactory) { + public TagsCollection(DynamicMap> views, Provider list) { this.views = views; this.list = list; - this.createTagFactory = createTagFactory; } @Override @@ -62,9 +55,4 @@ public class TagsCollection public DynamicMap> views() { return views; } - - @Override - public CreateTag create(ProjectResource resource, IdString name) { - return createTagFactory.create(name.get()); - } } diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java index 9dc9a50008..c83660d252 100644 --- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java +++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java @@ -20,6 +20,7 @@ import com.google.common.collect.Lists; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.api.accounts.AccountInput; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -66,7 +67,7 @@ final class CreateAccountCommand extends SshCommand { @Argument(index = 0, required = true, metaVar = "USERNAME", usage = "name of the user account") private String username; - @Inject private CreateAccount.Factory createAccountFactory; + @Inject private CreateAccount createAccount; @Override protected void run() throws OrmException, IOException, ConfigInvalidException, UnloggedFailure { @@ -78,7 +79,7 @@ final class CreateAccountCommand extends SshCommand { input.httpPassword = httpPassword; input.groups = Lists.transform(groups, AccountGroup.Id::toString); try { - createAccountFactory.create(username).apply(TopLevelResource.INSTANCE, input); + createAccount.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(username), input); } catch (RestApiException e) { throw die(e.getMessage()); } diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java index 5a83b01318..03f9616e4d 100644 --- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java +++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java @@ -91,7 +91,7 @@ final class CreateGroupCommand extends SshCommand { initialGroups.add(id); } - @Inject private CreateGroup.Factory createGroupFactory; + @Inject private CreateGroup createGroup; @Inject private GroupsCollection groups; @@ -126,7 +126,8 @@ final class CreateGroupCommand extends SshCommand { input.ownerId = String.valueOf(ownerGroupId.get()); } - GroupInfo group = createGroupFactory.create(groupName).apply(TopLevelResource.INSTANCE, input); + GroupInfo group = + createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName), input); return groups.parse(TopLevelResource.INSTANCE, IdString.fromUrl(group.id)); } diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java index 379fc6864a..852969f9df 100644 --- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java +++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java @@ -29,6 +29,7 @@ import com.google.gerrit.extensions.common.Input; import com.google.gerrit.extensions.common.NameInput; import com.google.gerrit.extensions.common.SshKeyInfo; import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Account; @@ -119,7 +120,7 @@ final class SetAccountCommand extends SshCommand { @Inject private IdentifiedUser.GenericFactory genericUserFactory; - @Inject private CreateEmail.Factory createEmailFactory; + @Inject private CreateEmail createEmail; @Inject private GetEmails getEmails; @@ -269,7 +270,7 @@ final class SetAccountCommand extends SshCommand { in.email = email; in.noConfirmation = true; try { - createEmailFactory.create(email).apply(rsrc, in); + createEmail.apply(rsrc, IdString.fromDecoded(email), in); } catch (EmailException e) { throw die(e.getMessage()); } diff --git a/javatests/com/google/gerrit/acceptance/rest/account/CreateAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/CreateAccountIT.java new file mode 100644 index 0000000000..aca6c4cbac --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/rest/account/CreateAccountIT.java @@ -0,0 +1,34 @@ +// Copyright (C) 2018 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.acceptance.rest.account; + +import static com.google.common.truth.Truth8.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.extensions.api.accounts.AccountInput; +import org.junit.Test; + +public class CreateAccountIT extends AbstractDaemonTest { + @Test + public void createAccountRestApi() throws Exception { + AccountInput input = new AccountInput(); + input.username = "foo"; + assertThat(accountCache.getByUsername(input.username)).isEmpty(); + RestResponse r = adminRestSession.put("/accounts/" + input.username, input); + r.assertCreated(); + assertThat(accountCache.getByUsername(input.username)).isPresent(); + } +} diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java index 7657e2e47a..4d499f0c4a 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java @@ -29,6 +29,7 @@ import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.common.ChangeInput; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.common.SuggestedReviewerInfo; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -42,7 +43,7 @@ import org.junit.Before; import org.junit.Test; public class SuggestReviewersIT extends AbstractDaemonTest { - @Inject private CreateGroup.Factory createGroupFactory; + @Inject private CreateGroup createGroup; private InternalGroup group1; private InternalGroup group2; @@ -490,7 +491,8 @@ public class SuggestReviewersIT extends AbstractDaemonTest { } private InternalGroup newGroup(String name) throws Exception { - GroupInfo group = createGroupFactory.create(name(name)).apply(TopLevelResource.INSTANCE, null); + GroupInfo group = + createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(name(name)), null); return group(new AccountGroup.UUID(group.id)); } diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java index 48dc99488b..0b7758eead 100644 --- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java @@ -15,12 +15,14 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; +import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS; import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GerritConfig; -import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.common.data.Permission; import com.google.gerrit.extensions.api.projects.BranchApi; import com.google.gerrit.extensions.api.projects.BranchInfo; @@ -35,7 +37,6 @@ import com.google.gerrit.reviewdb.client.RefNames; import org.junit.Before; import org.junit.Test; -@NoHttpd public class CreateBranchIT extends AbstractDaemonTest { private Branch.NameKey testBranch; @@ -44,6 +45,19 @@ public class CreateBranchIT extends AbstractDaemonTest { testBranch = new Branch.NameKey(project, "test"); } + @Test + public void createBranchRestApi() throws Exception { + BranchInput input = new BranchInput(); + input.ref = "foo"; + assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref)) + .doesNotContain(REFS_HEADS + input.ref); + RestResponse r = + adminRestSession.put("/projects/" + project.get() + "/branches/" + input.ref, input); + r.assertCreated(); + assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref)) + .contains(REFS_HEADS + input.ref); + } + @Test public void createBranch_Forbidden() throws Exception { setApiUser(user);