You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CreateAction.java 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.project.ws;
  21. import javax.annotation.CheckForNull;
  22. import javax.annotation.Nullable;
  23. import org.sonar.api.server.ws.Change;
  24. import org.sonar.api.server.ws.Request;
  25. import org.sonar.api.server.ws.Response;
  26. import org.sonar.api.server.ws.WebService;
  27. import org.sonar.db.DbClient;
  28. import org.sonar.db.DbSession;
  29. import org.sonar.db.component.ComponentDto;
  30. import org.sonar.db.organization.OrganizationDto;
  31. import org.sonar.server.component.ComponentUpdater;
  32. import org.sonar.server.project.Visibility;
  33. import org.sonar.server.user.UserSession;
  34. import org.sonarqube.ws.Projects.CreateWsResponse;
  35. import static org.apache.commons.lang.StringUtils.abbreviate;
  36. import static org.sonar.api.resources.Qualifiers.PROJECT;
  37. import static org.sonar.core.component.ComponentKeys.MAX_COMPONENT_KEY_LENGTH;
  38. import static org.sonar.db.component.ComponentValidator.MAX_COMPONENT_NAME_LENGTH;
  39. import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
  40. import static org.sonar.server.component.NewComponent.newComponentBuilder;
  41. import static org.sonar.server.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION;
  42. import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
  43. import static org.sonar.server.ws.WsUtils.writeProtobuf;
  44. import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE;
  45. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH;
  46. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;
  47. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
  48. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY;
  49. public class CreateAction implements ProjectsWsAction {
  50. private static final String DEPRECATED_PARAM_KEY = "key";
  51. private final ProjectsWsSupport support;
  52. private final DbClient dbClient;
  53. private final UserSession userSession;
  54. private final ComponentUpdater componentUpdater;
  55. public CreateAction(ProjectsWsSupport support, DbClient dbClient, UserSession userSession, ComponentUpdater componentUpdater) {
  56. this.support = support;
  57. this.dbClient = dbClient;
  58. this.userSession = userSession;
  59. this.componentUpdater = componentUpdater;
  60. }
  61. @Override
  62. public void define(WebService.NewController context) {
  63. WebService.NewAction action = context.createAction(ACTION_CREATE)
  64. .setDescription("Create a project.<br/>" +
  65. "Requires 'Create Projects' permission")
  66. .setSince("4.0")
  67. .setPost(true)
  68. .setResponseExample(getClass().getResource("create-example.json"))
  69. .setHandler(this);
  70. action.setChangelog(
  71. new Change("6.3", "The response format has been updated and does not contain the database ID anymore"),
  72. new Change("6.3", "The 'key' parameter has been renamed 'project'"),
  73. new Change("7.1", "The 'visibility' parameter is public"));
  74. action.createParam(PARAM_PROJECT)
  75. .setDescription("Key of the project")
  76. .setDeprecatedKey(DEPRECATED_PARAM_KEY, "6.3")
  77. .setRequired(true)
  78. .setMaximumLength(MAX_COMPONENT_KEY_LENGTH)
  79. .setExampleValue(KEY_PROJECT_EXAMPLE_001);
  80. action.createParam(PARAM_NAME)
  81. .setDescription("Name of the project. If name is longer than %d, it is abbreviated.", MAX_COMPONENT_NAME_LENGTH)
  82. .setRequired(true)
  83. .setExampleValue("SonarQube");
  84. action.createParam(PARAM_BRANCH)
  85. .setDescription("SCM Branch of the project. The key of the project will become key:branch, for instance 'SonarQube:branch-5.0'")
  86. .setExampleValue("branch-5.0");
  87. action.createParam(PARAM_VISIBILITY)
  88. .setDescription("Whether the created project should be visible to everyone, or only specific user/groups.<br/>" +
  89. "If no visibility is specified, the default project visibility of the organization will be used.")
  90. .setRequired(false)
  91. .setSince("6.4")
  92. .setPossibleValues(Visibility.getLabels());
  93. support.addOrganizationParam(action);
  94. }
  95. @Override
  96. public void handle(Request request, Response response) throws Exception {
  97. CreateRequest createRequest = toCreateRequest(request);
  98. writeProtobuf(doHandle(createRequest), request, response);
  99. }
  100. private CreateWsResponse doHandle(CreateRequest request) {
  101. try (DbSession dbSession = dbClient.openSession(false)) {
  102. OrganizationDto organization = support.getOrganization(dbSession, request.getOrganization());
  103. userSession.checkPermission(PROVISION_PROJECTS, organization);
  104. String visibility = request.getVisibility();
  105. boolean changeToPrivate = visibility == null ? dbClient.organizationDao().getNewProjectPrivate(dbSession, organization) : "private".equals(visibility);
  106. support.checkCanUpdateProjectsVisibility(organization, changeToPrivate);
  107. ComponentDto componentDto = componentUpdater.create(dbSession, newComponentBuilder()
  108. .setOrganizationUuid(organization.getUuid())
  109. .setKey(request.getKey())
  110. .setName(request.getName())
  111. .setDeprecatedBranch(request.getBranch())
  112. .setPrivate(changeToPrivate)
  113. .setQualifier(PROJECT)
  114. .build(),
  115. userSession.isLoggedIn() ? userSession.getUserId() : null);
  116. return toCreateResponse(componentDto);
  117. }
  118. }
  119. private static CreateRequest toCreateRequest(Request request) {
  120. return CreateRequest.builder()
  121. .setOrganization(request.param(PARAM_ORGANIZATION))
  122. .setKey(request.mandatoryParam(PARAM_PROJECT))
  123. .setName(abbreviate(request.mandatoryParam(PARAM_NAME), MAX_COMPONENT_NAME_LENGTH))
  124. .setBranch(request.param(PARAM_BRANCH))
  125. .setVisibility(request.param(PARAM_VISIBILITY))
  126. .build();
  127. }
  128. private static CreateWsResponse toCreateResponse(ComponentDto componentDto) {
  129. return CreateWsResponse.newBuilder()
  130. .setProject(CreateWsResponse.Project.newBuilder()
  131. .setKey(componentDto.getDbKey())
  132. .setName(componentDto.name())
  133. .setQualifier(componentDto.qualifier())
  134. .setVisibility(Visibility.getLabel(componentDto.isPrivate())))
  135. .build();
  136. }
  137. static class CreateRequest {
  138. private final String organization;
  139. private final String key;
  140. private final String name;
  141. private final String branch;
  142. @CheckForNull
  143. private final String visibility;
  144. private CreateRequest(Builder builder) {
  145. this.organization = builder.organization;
  146. this.key = builder.key;
  147. this.name = builder.name;
  148. this.branch = builder.branch;
  149. this.visibility = builder.visibility;
  150. }
  151. @CheckForNull
  152. public String getOrganization() {
  153. return organization;
  154. }
  155. @CheckForNull
  156. public String getKey() {
  157. return key;
  158. }
  159. @CheckForNull
  160. public String getName() {
  161. return name;
  162. }
  163. @CheckForNull
  164. public String getBranch() {
  165. return branch;
  166. }
  167. @CheckForNull
  168. public String getVisibility() {
  169. return visibility;
  170. }
  171. public static Builder builder() {
  172. return new Builder();
  173. }
  174. }
  175. static class Builder {
  176. private String organization;
  177. private String key;
  178. private String name;
  179. private String branch;
  180. @CheckForNull
  181. private String visibility;
  182. private Builder() {
  183. }
  184. public Builder setOrganization(@Nullable String organization) {
  185. this.organization = organization;
  186. return this;
  187. }
  188. public Builder setKey(@Nullable String key) {
  189. this.key = key;
  190. return this;
  191. }
  192. public Builder setName(@Nullable String name) {
  193. this.name = name;
  194. return this;
  195. }
  196. public Builder setBranch(@Nullable String branch) {
  197. this.branch = branch;
  198. return this;
  199. }
  200. public Builder setVisibility(@Nullable String visibility) {
  201. this.visibility = visibility;
  202. return this;
  203. }
  204. public CreateRequest build() {
  205. return new CreateRequest(this);
  206. }
  207. }
  208. }