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.

WebHooksImpl.java 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.webhook;
  21. import java.util.List;
  22. import java.util.Optional;
  23. import java.util.function.Supplier;
  24. import java.util.stream.Collectors;
  25. import java.util.stream.Stream;
  26. import javax.annotation.CheckForNull;
  27. import javax.annotation.Nullable;
  28. import org.sonar.api.ce.ComputeEngineSide;
  29. import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
  30. import org.sonar.api.server.ServerSide;
  31. import org.slf4j.Logger;
  32. import org.slf4j.LoggerFactory;
  33. import org.sonar.db.DbClient;
  34. import org.sonar.db.DbSession;
  35. import org.sonar.db.project.ProjectDto;
  36. import org.sonar.db.webhook.WebhookDao;
  37. import org.sonar.db.webhook.WebhookDto;
  38. import org.sonar.server.async.AsyncExecution;
  39. import static java.lang.String.format;
  40. @ServerSide
  41. @ComputeEngineSide
  42. public class WebHooksImpl implements WebHooks {
  43. private static final Logger LOGGER = LoggerFactory.getLogger(WebHooksImpl.class);
  44. private final WebhookCaller caller;
  45. private final WebhookDeliveryStorage deliveryStorage;
  46. private final AsyncExecution asyncExecution;
  47. private final DbClient dbClient;
  48. public WebHooksImpl(WebhookCaller caller, WebhookDeliveryStorage deliveryStorage, AsyncExecution asyncExecution, DbClient dbClient) {
  49. this.caller = caller;
  50. this.deliveryStorage = deliveryStorage;
  51. this.asyncExecution = asyncExecution;
  52. this.dbClient = dbClient;
  53. }
  54. @Override
  55. public boolean isEnabled(ProjectDto projectDto) {
  56. return readWebHooksFrom(projectDto.getUuid(), null)
  57. .findAny()
  58. .isPresent();
  59. }
  60. private Stream<WebhookDto> readWebHooksFrom(String projectUuid, @CheckForNull PostProjectAnalysisTask.LogStatistics taskLogStatistics) {
  61. try (DbSession dbSession = dbClient.openSession(false)) {
  62. Optional<ProjectDto> optionalProjectDto = dbClient.projectDao().selectByUuid(dbSession, projectUuid);
  63. ProjectDto projectDto = checkStateWithOptional(optionalProjectDto, "the requested project '%s' was not found", projectUuid);
  64. WebhookDao dao = dbClient.webhookDao();
  65. List<WebhookDto> projectWebhooks = dao.selectByProject(dbSession, projectDto);
  66. List<WebhookDto> globalWebhooks = dao.selectGlobalWebhooks(dbSession);
  67. if (taskLogStatistics != null) {
  68. taskLogStatistics.add("globalWebhooks", globalWebhooks.size());
  69. taskLogStatistics.add("projectWebhooks", projectWebhooks.size());
  70. }
  71. return Stream.concat(projectWebhooks.stream(), globalWebhooks.stream());
  72. }
  73. }
  74. private static <T> T checkStateWithOptional(Optional<T> value, String message, Object... messageArguments) {
  75. if (!value.isPresent()) {
  76. throw new IllegalStateException(format(message, messageArguments));
  77. }
  78. return value.get();
  79. }
  80. @Override
  81. public void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier) {
  82. sendProjectAnalysisUpdateImpl(analysis, payloadSupplier, null);
  83. }
  84. @Override
  85. public void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier, PostProjectAnalysisTask.LogStatistics taskLogStatistics) {
  86. sendProjectAnalysisUpdateImpl(analysis, payloadSupplier, taskLogStatistics);
  87. }
  88. private void sendProjectAnalysisUpdateImpl(Analysis analysis, Supplier<WebhookPayload> payloadSupplier,
  89. @Nullable PostProjectAnalysisTask.LogStatistics taskLogStatistics) {
  90. List<Webhook> webhooks = readWebHooksFrom(analysis.projectUuid(), taskLogStatistics)
  91. .map(dto -> new Webhook(dto.getUuid(), analysis.projectUuid(), analysis.ceTaskUuid(), analysis.analysisUuid(),
  92. dto.getName(), dto.getUrl(), dto.getSecret()))
  93. .toList();
  94. if (webhooks.isEmpty()) {
  95. return;
  96. }
  97. WebhookPayload payload = payloadSupplier.get();
  98. webhooks.forEach(webhook -> asyncExecution.addToQueue(() -> {
  99. WebhookDelivery delivery = caller.call(webhook, payload);
  100. log(delivery);
  101. deliveryStorage.persist(delivery);
  102. }));
  103. asyncExecution.addToQueue(() -> deliveryStorage.purge(analysis.projectUuid()));
  104. }
  105. private static void log(WebhookDelivery delivery) {
  106. Optional<String> error = delivery.getErrorMessage();
  107. if (error.isPresent()) {
  108. LOGGER.debug("Failed to send webhook '{}' | url={} | message={}",
  109. delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), error.get());
  110. } else {
  111. LOGGER.debug("Sent webhook '{}' | url={} | time={}ms | status={}",
  112. delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), delivery.getDurationInMs().orElse(-1), delivery.getHttpStatus().orElse(-1));
  113. }
  114. }
  115. }