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.

TaskAction.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 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.ce.ws;
  21. import java.util.Arrays;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Objects;
  26. import java.util.Optional;
  27. import java.util.Set;
  28. import javax.annotation.CheckForNull;
  29. import javax.annotation.Nullable;
  30. import org.sonar.api.server.ws.Change;
  31. import org.sonar.api.server.ws.Request;
  32. import org.sonar.api.server.ws.Response;
  33. import org.sonar.api.server.ws.WebService;
  34. import org.sonar.api.web.UserRole;
  35. import org.sonar.core.util.Uuids;
  36. import org.sonar.core.util.stream.MoreCollectors;
  37. import org.sonar.db.DbClient;
  38. import org.sonar.db.DbSession;
  39. import org.sonar.db.ce.CeActivityDto;
  40. import org.sonar.db.ce.CeQueueDto;
  41. import org.sonar.db.ce.CeTaskMessageDto;
  42. import org.sonar.db.component.ComponentDto;
  43. import org.sonar.db.permission.OrganizationPermission;
  44. import org.sonar.server.user.UserSession;
  45. import org.sonar.server.ws.WsUtils;
  46. import org.sonarqube.ws.Ce;
  47. import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
  48. import static org.sonar.server.ws.WsUtils.writeProtobuf;
  49. public class TaskAction implements CeWsAction {
  50. public static final String ACTION = "task";
  51. public static final String PARAM_TASK_UUID = "id";
  52. private static final String PARAM_ADDITIONAL_FIELDS = "additionalFields";
  53. private final DbClient dbClient;
  54. private final TaskFormatter wsTaskFormatter;
  55. private final UserSession userSession;
  56. public TaskAction(DbClient dbClient, TaskFormatter wsTaskFormatter, UserSession userSession) {
  57. this.dbClient = dbClient;
  58. this.wsTaskFormatter = wsTaskFormatter;
  59. this.userSession = userSession;
  60. }
  61. @Override
  62. public void define(WebService.NewController controller) {
  63. WebService.NewAction action = controller.createAction(ACTION)
  64. .setDescription("Give Compute Engine task details such as type, status, duration and associated component.<br />" +
  65. "Requires 'Administer System' or 'Execute Analysis' permission.<br/>" +
  66. "Since 6.1, field \"logs\" is deprecated and its value is always false.")
  67. .setResponseExample(getClass().getResource("task-example.json"))
  68. .setSince("5.2")
  69. .setChangelog(
  70. new Change("6.6", "fields \"branch\" and \"branchType\" added"))
  71. .setHandler(this);
  72. action
  73. .createParam(PARAM_TASK_UUID)
  74. .setRequired(true)
  75. .setDescription("Id of task")
  76. .setExampleValue(Uuids.UUID_EXAMPLE_01);
  77. action.createParam(PARAM_ADDITIONAL_FIELDS)
  78. .setSince("6.1")
  79. .setDescription("Comma-separated list of the optional fields to be returned in response.")
  80. .setPossibleValues(AdditionalField.possibleValues());
  81. }
  82. @Override
  83. public void handle(Request wsRequest, Response wsResponse) throws Exception {
  84. String taskUuid = wsRequest.mandatoryParam(PARAM_TASK_UUID);
  85. try (DbSession dbSession = dbClient.openSession(false)) {
  86. Ce.TaskResponse.Builder wsTaskResponse = Ce.TaskResponse.newBuilder();
  87. Optional<CeQueueDto> queueDto = dbClient.ceQueueDao().selectByUuid(dbSession, taskUuid);
  88. if (queueDto.isPresent()) {
  89. Optional<ComponentDto> component = loadComponent(dbSession, queueDto.get().getComponentUuid());
  90. checkPermission(component);
  91. wsTaskResponse.setTask(wsTaskFormatter.formatQueue(dbSession, queueDto.get()));
  92. } else {
  93. CeActivityDto ceActivityDto = WsUtils.checkFoundWithOptional(dbClient.ceActivityDao().selectByUuid(dbSession, taskUuid), "No activity found for task '%s'", taskUuid);
  94. Optional<ComponentDto> component = loadComponent(dbSession, ceActivityDto.getComponentUuid());
  95. checkPermission(component);
  96. Set<AdditionalField> additionalFields = AdditionalField.getFromRequest(wsRequest);
  97. maskErrorStacktrace(ceActivityDto, additionalFields);
  98. wsTaskResponse.setTask(
  99. wsTaskFormatter.formatActivity(dbSession, ceActivityDto,
  100. extractScannerContext(dbSession, ceActivityDto, additionalFields),
  101. extractWarnings(dbSession, ceActivityDto, additionalFields)));
  102. }
  103. writeProtobuf(wsTaskResponse.build(), wsRequest, wsResponse);
  104. }
  105. }
  106. private Optional<ComponentDto> loadComponent(DbSession dbSession, @Nullable String projectUuid) {
  107. if (projectUuid == null) {
  108. return Optional.empty();
  109. }
  110. return dbClient.componentDao().selectByUuid(dbSession, projectUuid);
  111. }
  112. private void checkPermission(Optional<ComponentDto> component) {
  113. if (component.isPresent()) {
  114. String orgUuid = component.get().getOrganizationUuid();
  115. if (!userSession.hasPermission(OrganizationPermission.ADMINISTER, orgUuid) &&
  116. !userSession.hasPermission(OrganizationPermission.SCAN, orgUuid) &&
  117. !userSession.hasComponentPermission(UserRole.SCAN, component.get())) {
  118. throw insufficientPrivilegesException();
  119. }
  120. } else {
  121. userSession.checkIsSystemAdministrator();
  122. }
  123. }
  124. private static void maskErrorStacktrace(CeActivityDto ceActivityDto, Set<AdditionalField> additionalFields) {
  125. if (!additionalFields.contains(AdditionalField.STACKTRACE)) {
  126. ceActivityDto.setErrorStacktrace(null);
  127. }
  128. }
  129. @CheckForNull
  130. private String extractScannerContext(DbSession dbSession, CeActivityDto activityDto, Set<AdditionalField> additionalFields) {
  131. if (additionalFields.contains(AdditionalField.SCANNER_CONTEXT)) {
  132. return dbClient.ceScannerContextDao().selectScannerContext(dbSession, activityDto.getUuid())
  133. .orElse(null);
  134. }
  135. return null;
  136. }
  137. private List<String> extractWarnings(DbSession dbSession, CeActivityDto activityDto, Set<AdditionalField> additionalFields) {
  138. if (additionalFields.contains(AdditionalField.WARNINGS)) {
  139. List<CeTaskMessageDto> dtos = dbClient.ceTaskMessageDao().selectByTask(dbSession, activityDto.getUuid());
  140. return dtos.stream()
  141. .map(CeTaskMessageDto::getMessage)
  142. .collect(MoreCollectors.toList(dtos.size()));
  143. }
  144. return Collections.emptyList();
  145. }
  146. private enum AdditionalField {
  147. STACKTRACE("stacktrace"),
  148. SCANNER_CONTEXT("scannerContext"),
  149. WARNINGS("warnings");
  150. private final String label;
  151. AdditionalField(String label) {
  152. this.label = label;
  153. }
  154. public String getLabel() {
  155. return label;
  156. }
  157. public static Set<AdditionalField> getFromRequest(Request wsRequest) {
  158. List<String> strings = wsRequest.paramAsStrings(PARAM_ADDITIONAL_FIELDS);
  159. if (strings == null) {
  160. return Collections.emptySet();
  161. }
  162. return strings.stream()
  163. .map(s -> {
  164. for (AdditionalField field : AdditionalField.values()) {
  165. if (field.label.equalsIgnoreCase(s)) {
  166. return field;
  167. }
  168. }
  169. return null;
  170. })
  171. .filter(Objects::nonNull)
  172. .collect(MoreCollectors.toSet());
  173. }
  174. public static Collection<String> possibleValues() {
  175. return Arrays.stream(values())
  176. .map(AdditionalField::getLabel)
  177. .collect(MoreCollectors.toList(values().length));
  178. }
  179. }
  180. }