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.

TaskFormatter.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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 com.google.common.collect.Multimap;
  22. import java.util.Collection;
  23. import java.util.Date;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Objects;
  27. import java.util.Optional;
  28. import java.util.Set;
  29. import java.util.stream.Collectors;
  30. import javax.annotation.CheckForNull;
  31. import javax.annotation.Nullable;
  32. import org.sonar.api.utils.DateUtils;
  33. import org.sonar.api.utils.System2;
  34. import org.sonar.core.util.stream.MoreCollectors;
  35. import org.sonar.db.DbClient;
  36. import org.sonar.db.DbSession;
  37. import org.sonar.db.ce.CeActivityDto;
  38. import org.sonar.db.ce.CeQueueDto;
  39. import org.sonar.db.ce.CeTaskCharacteristicDto;
  40. import org.sonar.db.component.ComponentDto;
  41. import org.sonar.db.organization.OrganizationDto;
  42. import org.sonar.db.user.UserDto;
  43. import org.sonarqube.ws.Ce;
  44. import org.sonarqube.ws.Common;
  45. import static com.google.common.base.Preconditions.checkState;
  46. import static java.lang.String.format;
  47. import static java.util.Collections.emptyList;
  48. import static java.util.Collections.singletonList;
  49. import static java.util.Optional.ofNullable;
  50. import static org.sonar.api.utils.DateUtils.formatDateTime;
  51. import static org.sonar.core.util.stream.MoreCollectors.toSet;
  52. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  53. /**
  54. * Converts {@link CeActivityDto} and {@link CeQueueDto} to the protobuf objects
  55. * used to write WS responses (see ws-ce.proto in module sonar-ws)
  56. */
  57. public class TaskFormatter {
  58. private final DbClient dbClient;
  59. private final System2 system2;
  60. public TaskFormatter(DbClient dbClient, System2 system2) {
  61. this.dbClient = dbClient;
  62. this.system2 = system2;
  63. }
  64. public List<Ce.Task> formatQueue(DbSession dbSession, List<CeQueueDto> dtos) {
  65. DtoCache cache = DtoCache.forQueueDtos(dbClient, dbSession, dtos);
  66. return dtos.stream().map(input -> formatQueue(input, cache)).collect(MoreCollectors.toList(dtos.size()));
  67. }
  68. public Ce.Task formatQueue(DbSession dbSession, CeQueueDto queue) {
  69. return formatQueue(queue, DtoCache.forQueueDtos(dbClient, dbSession, singletonList(queue)));
  70. }
  71. private Ce.Task formatQueue(CeQueueDto dto, DtoCache cache) {
  72. Ce.Task.Builder builder = Ce.Task.newBuilder();
  73. String organizationKey = cache.getOrganizationKey(dto.getComponentUuid());
  74. ofNullable(organizationKey).ifPresent(builder::setOrganization);
  75. if (dto.getComponentUuid() != null) {
  76. builder.setComponentId(dto.getComponentUuid());
  77. setComponent(builder, dto.getComponentUuid(), cache);
  78. }
  79. builder.setId(dto.getUuid());
  80. builder.setStatus(Ce.TaskStatus.valueOf(dto.getStatus().name()));
  81. builder.setType(dto.getTaskType());
  82. builder.setLogs(false);
  83. cache.getUser(dto.getSubmitterUuid()).ifPresent(user -> builder.setSubmitterLogin(user.getLogin()));
  84. builder.setSubmittedAt(formatDateTime(new Date(dto.getCreatedAt())));
  85. ofNullable(dto.getStartedAt()).map(DateUtils::formatDateTime).ifPresent(builder::setStartedAt);
  86. ofNullable(computeExecutionTimeMs(dto)).ifPresent(builder::setExecutionTimeMs);
  87. setBranchOrPullRequest(builder, dto.getUuid(), cache);
  88. return builder.build();
  89. }
  90. public Ce.Task formatActivity(DbSession dbSession, CeActivityDto dto, @Nullable String scannerContext, List<String> warnings) {
  91. return formatActivity(dto, DtoCache.forActivityDtos(dbClient, dbSession, singletonList(dto)), scannerContext, warnings);
  92. }
  93. public List<Ce.Task> formatActivity(DbSession dbSession, List<CeActivityDto> dtos) {
  94. DtoCache cache = DtoCache.forActivityDtos(dbClient, dbSession, dtos);
  95. return dtos.stream()
  96. .map(input -> formatActivity(input, cache, null, emptyList()))
  97. .collect(MoreCollectors.toList(dtos.size()));
  98. }
  99. private static Ce.Task formatActivity(CeActivityDto dto, DtoCache cache, @Nullable String scannerContext, List<String> warnings) {
  100. Ce.Task.Builder builder = Ce.Task.newBuilder();
  101. String organizationKey = cache.getOrganizationKey(dto.getComponentUuid());
  102. ofNullable(organizationKey).ifPresent(builder::setOrganization);
  103. builder.setId(dto.getUuid());
  104. builder.setStatus(Ce.TaskStatus.valueOf(dto.getStatus().name()));
  105. builder.setType(dto.getTaskType());
  106. builder.setLogs(false);
  107. ofNullable(dto.getComponentUuid()).ifPresent(uuid -> setComponent(builder, uuid, cache).setComponentId(uuid));
  108. String analysisUuid = dto.getAnalysisUuid();
  109. ofNullable(analysisUuid).ifPresent(builder::setAnalysisId);
  110. setBranchOrPullRequest(builder, dto.getUuid(), cache);
  111. ofNullable(analysisUuid).ifPresent(builder::setAnalysisId);
  112. cache.getUser(dto.getSubmitterUuid()).ifPresent(user -> builder.setSubmitterLogin(user.getLogin()));
  113. builder.setSubmittedAt(formatDateTime(new Date(dto.getSubmittedAt())));
  114. ofNullable(dto.getStartedAt()).map(DateUtils::formatDateTime).ifPresent(builder::setStartedAt);
  115. ofNullable(dto.getExecutedAt()).map(DateUtils::formatDateTime).ifPresent(builder::setExecutedAt);
  116. ofNullable(dto.getExecutionTimeMs()).ifPresent(builder::setExecutionTimeMs);
  117. ofNullable(dto.getErrorMessage()).ifPresent(builder::setErrorMessage);
  118. ofNullable(dto.getErrorStacktrace()).ifPresent(builder::setErrorStacktrace);
  119. ofNullable(dto.getErrorType()).ifPresent(builder::setErrorType);
  120. ofNullable(scannerContext).ifPresent(builder::setScannerContext);
  121. builder.setHasScannerContext(dto.isHasScannerContext());
  122. builder.setWarningCount(dto.getWarningCount());
  123. warnings.forEach(builder::addWarnings);
  124. return builder.build();
  125. }
  126. private static Ce.Task.Builder setComponent(Ce.Task.Builder builder, @Nullable String componentUuid, DtoCache componentDtoCache) {
  127. ComponentDto componentDto = componentDtoCache.getComponent(componentUuid);
  128. if (componentDto == null) {
  129. return builder;
  130. }
  131. builder.setComponentKey(componentDto.getKey());
  132. builder.setComponentName(componentDto.name());
  133. builder.setComponentQualifier(componentDto.qualifier());
  134. return builder;
  135. }
  136. private static Ce.Task.Builder setBranchOrPullRequest(Ce.Task.Builder builder, String taskUuid, DtoCache componentDtoCache) {
  137. componentDtoCache.getBranchKey(taskUuid).ifPresent(
  138. b -> {
  139. Common.BranchType branchType = componentDtoCache.getBranchType(taskUuid)
  140. .orElseThrow(() -> new IllegalStateException(format("Could not find branch type of task '%s'", taskUuid)));
  141. switch (branchType) {
  142. case LONG:
  143. case SHORT:
  144. builder.setBranchType(branchType);
  145. builder.setBranch(b);
  146. break;
  147. default:
  148. throw new IllegalStateException(String.format("Unknown branch type '%s'", branchType));
  149. }
  150. });
  151. componentDtoCache.getPullRequest(taskUuid).ifPresent(builder::setPullRequest);
  152. return builder;
  153. }
  154. private static class DtoCache {
  155. private final Map<String, ComponentDto> componentsByUuid;
  156. private final Map<String, OrganizationDto> organizationsByUuid;
  157. private final Multimap<String, CeTaskCharacteristicDto> characteristicsByTaskUuid;
  158. private final Map<String, UserDto> usersByUuid;
  159. private DtoCache(Map<String, ComponentDto> componentsByUuid, Map<String, OrganizationDto> organizationsByUuid,
  160. Multimap<String, CeTaskCharacteristicDto> characteristicsByTaskUuid, Map<String, UserDto> usersByUuid) {
  161. this.componentsByUuid = componentsByUuid;
  162. this.organizationsByUuid = organizationsByUuid;
  163. this.characteristicsByTaskUuid = characteristicsByTaskUuid;
  164. this.usersByUuid = usersByUuid;
  165. }
  166. static DtoCache forQueueDtos(DbClient dbClient, DbSession dbSession, Collection<CeQueueDto> ceQueueDtos) {
  167. Map<String, ComponentDto> componentsByUuid = dbClient.componentDao().selectByUuids(dbSession, componentUuidsOfCeQueues(ceQueueDtos))
  168. .stream()
  169. .collect(uniqueIndex(ComponentDto::uuid));
  170. Multimap<String, CeTaskCharacteristicDto> characteristicsByTaskUuid = dbClient.ceTaskCharacteristicsDao()
  171. .selectByTaskUuids(dbSession, ceQueueDtos.stream().map(CeQueueDto::getUuid).collect(Collectors.toList()))
  172. .stream().collect(MoreCollectors.index(CeTaskCharacteristicDto::getTaskUuid));
  173. Set<String> submitterUuids = ceQueueDtos.stream().map(CeQueueDto::getSubmitterUuid).filter(Objects::nonNull).collect(toSet());
  174. Map<String, UserDto> usersByUuid = dbClient.userDao().selectByUuids(dbSession, submitterUuids).stream().collect(uniqueIndex(UserDto::getUuid));
  175. return new DtoCache(componentsByUuid, buildOrganizationsByUuid(dbClient, dbSession, componentsByUuid), characteristicsByTaskUuid, usersByUuid);
  176. }
  177. private static Set<String> componentUuidsOfCeQueues(Collection<CeQueueDto> ceQueueDtos) {
  178. return ceQueueDtos.stream()
  179. .filter(Objects::nonNull)
  180. .map(CeQueueDto::getComponentUuid)
  181. .filter(Objects::nonNull)
  182. .collect(toSet(ceQueueDtos.size()));
  183. }
  184. static DtoCache forActivityDtos(DbClient dbClient, DbSession dbSession, Collection<CeActivityDto> ceActivityDtos) {
  185. Map<String, ComponentDto> componentsByUuid = dbClient.componentDao().selectByUuids(
  186. dbSession,
  187. getComponentUuidsOfCeActivities(ceActivityDtos))
  188. .stream()
  189. .collect(uniqueIndex(ComponentDto::uuid));
  190. Multimap<String, CeTaskCharacteristicDto> characteristicsByTaskUuid = dbClient.ceTaskCharacteristicsDao()
  191. .selectByTaskUuids(dbSession, ceActivityDtos.stream().map(CeActivityDto::getUuid).collect(Collectors.toList()))
  192. .stream().collect(MoreCollectors.index(CeTaskCharacteristicDto::getTaskUuid));
  193. Set<String> submitterUuids = ceActivityDtos.stream().map(CeActivityDto::getSubmitterUuid).filter(Objects::nonNull).collect(toSet());
  194. Map<String, UserDto> usersByUuid = dbClient.userDao().selectByUuids(dbSession, submitterUuids).stream().collect(uniqueIndex(UserDto::getUuid));
  195. return new DtoCache(componentsByUuid, buildOrganizationsByUuid(dbClient, dbSession, componentsByUuid), characteristicsByTaskUuid, usersByUuid);
  196. }
  197. private static Set<String> getComponentUuidsOfCeActivities(Collection<CeActivityDto> ceActivityDtos) {
  198. return ceActivityDtos.stream()
  199. .filter(Objects::nonNull)
  200. .map(CeActivityDto::getComponentUuid)
  201. .filter(Objects::nonNull)
  202. .collect(toSet(ceActivityDtos.size()));
  203. }
  204. private static Map<String, OrganizationDto> buildOrganizationsByUuid(DbClient dbClient, DbSession dbSession, Map<String, ComponentDto> componentsByUuid) {
  205. return dbClient.organizationDao().selectByUuids(
  206. dbSession,
  207. componentsByUuid.values().stream()
  208. .map(ComponentDto::getOrganizationUuid)
  209. .collect(toSet(componentsByUuid.size())))
  210. .stream()
  211. .collect(uniqueIndex(OrganizationDto::getUuid));
  212. }
  213. @CheckForNull
  214. ComponentDto getComponent(@Nullable String uuid) {
  215. if (uuid == null) {
  216. return null;
  217. }
  218. return componentsByUuid.get(uuid);
  219. }
  220. @CheckForNull
  221. String getOrganizationKey(@Nullable String componentUuid) {
  222. if (componentUuid == null) {
  223. return null;
  224. }
  225. ComponentDto componentDto = componentsByUuid.get(componentUuid);
  226. if (componentDto == null) {
  227. return null;
  228. }
  229. String organizationUuid = componentDto.getOrganizationUuid();
  230. OrganizationDto organizationDto = organizationsByUuid.get(organizationUuid);
  231. checkState(organizationDto != null, "Organization with uuid '%s' not found", organizationUuid);
  232. return organizationDto.getKey();
  233. }
  234. Optional<String> getBranchKey(String taskUuid) {
  235. return characteristicsByTaskUuid.get(taskUuid).stream()
  236. .filter(c -> c.getKey().equals(CeTaskCharacteristicDto.BRANCH_KEY))
  237. .map(CeTaskCharacteristicDto::getValue)
  238. .findAny();
  239. }
  240. Optional<Common.BranchType> getBranchType(String taskUuid) {
  241. return characteristicsByTaskUuid.get(taskUuid).stream()
  242. .filter(c -> c.getKey().equals(CeTaskCharacteristicDto.BRANCH_TYPE_KEY))
  243. .map(c -> Common.BranchType.valueOf(c.getValue()))
  244. .findAny();
  245. }
  246. Optional<String> getPullRequest(String taskUuid) {
  247. return characteristicsByTaskUuid.get(taskUuid).stream()
  248. .filter(c -> c.getKey().equals(CeTaskCharacteristicDto.PULL_REQUEST))
  249. .map(CeTaskCharacteristicDto::getValue)
  250. .findAny();
  251. }
  252. Optional<UserDto> getUser(@Nullable String userUuid) {
  253. if (userUuid == null) {
  254. return Optional.empty();
  255. }
  256. return Optional.ofNullable(usersByUuid.get(userUuid));
  257. }
  258. }
  259. /**
  260. * now - startedAt
  261. */
  262. @CheckForNull
  263. private Long computeExecutionTimeMs(CeQueueDto dto) {
  264. Long startedAt = dto.getStartedAt();
  265. if (startedAt == null) {
  266. return null;
  267. }
  268. return system2.now() - startedAt;
  269. }
  270. }