import org.sonar.server.view.index.ViewIndex;
import org.sonar.server.view.index.ViewIndexDefinition;
import org.sonar.server.view.index.ViewIndexer;
+import org.sonar.server.webhook.ws.WebhooksWsModule;
import org.sonar.server.ws.WebServiceEngine;
import org.sonar.server.ws.WebServiceFilter;
import org.sonar.server.ws.WebServicesWs;
NavigationWs.class,
// root
- RootWsModule.class);
+ RootWsModule.class,
+
+ // webhooks
+ WebhooksWsModule.class);
addAll(level4AddedComponents);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import com.google.common.io.Resources;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDeliveryLiteDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.Webhooks;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class WebhookDeliveriesAction implements WebhooksWsAction {
+
+ private static final String COMPONENT_PARAM = "componentKey";
+ private static final String TASK_PARAM = "ceTaskId";
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+
+ public WebhookDeliveriesAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("deliveries")
+ .setSince("6.2")
+ .setDescription("Get the recent deliveries for a specified project or Compute Engine task.<br/>" +
+ "Require 'Administer System' permission.<br/>" +
+ "Note that additional information are returned by api/webhooks/delivery.")
+ .setResponseExample(Resources.getResource(this.getClass(), "example-deliveries.json"))
+ .setInternal(true)
+ .setHandler(this);
+
+ action.createParam(COMPONENT_PARAM)
+ .setDescription("Key of the project")
+ .setExampleValue("my-project");
+
+ action.createParam(TASK_PARAM)
+ .setDescription("Id of the Compute Engine task")
+ .setExampleValue(Uuids.UUID_EXAMPLE_01);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ // fail-fast if not logged in
+ userSession.checkLoggedIn();
+
+ String ceTaskId = request.param(TASK_PARAM);
+ String componentKey = request.param(COMPONENT_PARAM);
+ checkArgument(ceTaskId != null ^ componentKey != null, "Either parameter '%s' or '%s' must be defined", TASK_PARAM, COMPONENT_PARAM);
+
+ Data data = loadFromDatabase(ceTaskId, componentKey);
+ data.ensureAdminPermission(userSession);
+ data.writeTo(request, response);
+ }
+
+ private Data loadFromDatabase(@Nullable String ceTaskId, @Nullable String componentKey) {
+ ComponentDto component = null;
+ List<WebhookDeliveryLiteDto> deliveries;
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ if (componentKey != null) {
+ component = componentFinder.getByKey(dbSession, componentKey);
+ deliveries = dbClient.webhookDeliveryDao().selectOrderedByComponentUuid(dbSession, component.uuid());
+ } else {
+ deliveries = dbClient.webhookDeliveryDao().selectOrderedByCeTaskUuid(dbSession, ceTaskId);
+ Optional<String> deliveredComponentUuid = deliveries
+ .stream()
+ .map(WebhookDeliveryLiteDto::getComponentUuid)
+ .findFirst();
+ if (deliveredComponentUuid.isPresent()) {
+ component = componentFinder.getByUuid(dbSession, deliveredComponentUuid.get());
+ }
+ }
+ }
+ return new Data(component, deliveries);
+ }
+
+ private static class Data {
+ private final ComponentDto component;
+ private final List<WebhookDeliveryLiteDto> deliveries;
+
+ Data(@Nullable ComponentDto component, List<WebhookDeliveryLiteDto> deliveries) {
+ this.deliveries = deliveries;
+ if (deliveries.isEmpty()) {
+ this.component = null;
+ } else {
+ this.component = requireNonNull(component);
+ }
+ }
+
+ void ensureAdminPermission(UserSession userSession) {
+ if (component != null) {
+ userSession.checkComponentUuidPermission(UserRole.ADMIN, component.uuid());
+ }
+ }
+
+ void writeTo(Request request, Response response) {
+ Webhooks.DeliveriesWsResponse.Builder responseBuilder = Webhooks.DeliveriesWsResponse.newBuilder();
+ Webhooks.Delivery.Builder deliveryBuilder = Webhooks.Delivery.newBuilder();
+ for (WebhookDeliveryLiteDto dto : deliveries) {
+ deliveryBuilder
+ .clear()
+ .setId(dto.getUuid())
+ .setAt(formatDateTime(dto.getCreatedAt()))
+ .setName(dto.getName())
+ .setUrl(dto.getUrl())
+ .setSuccess(dto.isSuccess())
+ .setCeTaskId(dto.getCeTaskUuid())
+ .setComponentKey(component.getKey());
+ if (dto.getHttpStatus() != null) {
+ deliveryBuilder.setHttpStatus(dto.getHttpStatus());
+ }
+ if (dto.getDurationMs() != null) {
+ deliveryBuilder.setDurationMs(dto.getDurationMs());
+ }
+ responseBuilder.addDeliveries(deliveryBuilder);
+ }
+ writeProtobuf(responseBuilder.build(), request, response);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.sonar.api.server.ws.WebService;
+
+public class WebhooksWs implements WebService {
+
+ public static final String API_ENDPOINT = "api/webhooks";
+
+ private final WebhooksWsAction[] actions;
+
+ public WebhooksWs(WebhooksWsAction... actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController(API_ENDPOINT);
+ controller.setDescription("Webhooks allow to notify external services when a project analysis is done");
+ controller.setSince("6.2");
+ for (WebhooksWsAction action : actions) {
+ action.define(controller);
+ }
+ controller.done();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.sonar.server.ws.WsAction;
+
+interface WebhooksWsAction extends WsAction {
+ // Marker interface
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.sonar.core.platform.Module;
+
+public class WebhooksWsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ WebhooksWs.class,
+ WebhookDeliveriesAction.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.webhook.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+{
+ "deliveries": [
+ {
+ "id": "d1",
+ "componentKey": "my-project",
+ "ceTaskId": "task-1",
+ "name": "Jenkins",
+ "url": "http://jenkins",
+ "at": "2017-07-14T04:40:00+0200",
+ "success": true,
+ "httpStatus": 200,
+ "durationMs": 10
+ }
+ ]
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDeliveryDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Webhooks;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class WebhookDeliveriesActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private DbClient dbClient = db.getDbClient();
+ private WsActionTester ws;
+ private ComponentDto project;
+
+ @Before
+ public void setUp() {
+ ComponentFinder componentFinder = new ComponentFinder(dbClient);
+ WebhookDeliveriesAction underTest = new WebhookDeliveriesAction(dbClient, userSession, componentFinder);
+ ws = new WsActionTester(underTest);
+ project = db.components().insertComponent(newProjectDto().setKey("my-project"));
+ }
+
+ @Test
+ public void test_definition() {
+ assertThat(ws.getDef().params()).extracting(WebService.Param::key).containsOnly("componentKey", "ceTaskId");
+ assertThat(ws.getDef().isPost()).isFalse();
+ assertThat(ws.getDef().isInternal()).isTrue();
+ assertThat(ws.getDef().responseExampleAsString()).isNotEmpty();
+ }
+
+ @Test
+ public void throw_UnauthorizedException_if_anonymous() {
+ expectedException.expect(UnauthorizedException.class);
+
+ ws.newRequest().execute();
+ }
+
+ @Test
+ public void search_by_component_and_return_no_records() throws Exception {
+ userSession.login().addProjectUuidPermissions(project.uuid(), UserRole.ADMIN);
+
+ Webhooks.DeliveriesWsResponse response = Webhooks.DeliveriesWsResponse.parseFrom(ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam("componentKey", project.getKey())
+ .execute()
+ .getInputStream());
+
+ assertThat(response.getDeliveriesCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void search_by_task_and_return_no_records() throws Exception {
+ userSession.login().addProjectUuidPermissions(project.uuid(), UserRole.ADMIN);
+
+ Webhooks.DeliveriesWsResponse response = Webhooks.DeliveriesWsResponse.parseFrom(ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam("ceTaskId", "t1")
+ .execute()
+ .getInputStream());
+
+ assertThat(response.getDeliveriesCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void search_by_component_and_return_records_of_example() throws Exception {
+ WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ .setUuid("d1")
+ .setComponentUuid(project.uuid())
+ .setCeTaskUuid("task-1")
+ .setName("Jenkins")
+ .setUrl("http://jenkins")
+ .setCreatedAt(1_500_000_000_000L)
+ .setSuccess(true)
+ .setDurationMs(10)
+ .setHttpStatus(200);
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
+ db.commit();
+ userSession.login().addProjectUuidPermissions(UserRole.ADMIN, project.uuid());
+
+ String json = ws.newRequest()
+ .setParam("componentKey", project.getKey())
+ .execute()
+ .getInput();
+
+ assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
+ }
+
+ @Test
+ public void search_by_task_and_return_records() throws Exception {
+ WebhookDeliveryDto dto1 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
+ WebhookDeliveryDto dto2 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
+ WebhookDeliveryDto dto3 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t2");
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto1);
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto2);
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto3);
+ db.commit();
+ userSession.login().addProjectUuidPermissions(UserRole.ADMIN, project.uuid());
+
+ Webhooks.DeliveriesWsResponse response = Webhooks.DeliveriesWsResponse.parseFrom(ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam("ceTaskId", "t1")
+ .execute()
+ .getInputStream());
+ assertThat(response.getDeliveriesCount()).isEqualTo(2);
+ assertThat(response.getDeliveriesList()).extracting(Webhooks.Delivery::getId).containsOnly(dto1.getUuid(), dto2.getUuid());
+ }
+
+ @Test
+ public void search_by_component_and_throw_ForbiddenException_if_not_admin_of_project() throws Exception {
+ WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ .setComponentUuid(project.uuid());
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
+ db.commit();
+ userSession.login().addProjectUuidPermissions(UserRole.USER, project.uuid());
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ ws.newRequest()
+ .setParam("componentKey", project.getKey())
+ .execute();
+ }
+
+ @Test
+ public void search_by_task_and_throw_ForbiddenException_if_not_admin_of_project() throws Exception {
+ WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ .setComponentUuid(project.uuid());
+ dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
+ db.commit();
+ userSession.login().addProjectUuidPermissions(UserRole.USER, project.uuid());
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ ws.newRequest()
+ .setParam("ceTaskId", dto.getCeTaskUuid())
+ .execute();
+ }
+
+ @Test
+ public void throw_IAE_if_both_component_and_task_parameters_are_set() throws Exception {
+ userSession.login();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Either parameter 'ceTaskId' or 'componentKey' must be defined");
+
+ ws.newRequest()
+ .setParam("componentKey", project.getKey())
+ .setParam("ceTaskId", "t1")
+ .execute();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebhooksWsModuleTest {
+
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+ new WebhooksWsModule().configure(container);
+ assertThat(container.size()).isEqualTo(2 + 2);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook.ws;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class WebhooksWsTest {
+
+ @Test
+ public void test_definition() {
+ WebhooksWsAction action = newFakeAction();
+ WebhooksWs underTest = new WebhooksWs(action);
+
+ WebService.Context context = new WebService.Context();
+ underTest.define(context);
+
+ WebService.Controller controller = context.controller("api/webhooks");
+ assertThat(controller).isNotNull();
+ assertThat(controller.description()).isNotEmpty();
+ assertThat(controller.since()).isEqualTo("6.2");
+ }
+
+ private static WebhooksWsAction newFakeAction() {
+ return new WebhookDeliveriesAction(mock(DbClient.class), mock(UserSession.class), mock(ComponentFinder.class));
+ }
+
+}
*/
package org.sonar.db.webhook;
+import java.util.List;
import java.util.Optional;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) {
return Optional.ofNullable(mapper(dbSession).selectByUuid(uuid));
}
+
+ /**
+ * All the deliveries for the specified component. Results are ordered by descending date.
+ */
+ public List<WebhookDeliveryLiteDto> selectOrderedByComponentUuid(DbSession dbSession, String componentUuid) {
+ return mapper(dbSession).selectOrderedByComponentUuid(componentUuid);
+ }
+
+ /**
+ * All the deliveries for the specified CE task. Results are ordered by descending date.
+ */
+ public List<WebhookDeliveryLiteDto> selectOrderedByCeTaskUuid(DbSession dbSession, String ceTaskUuid) {
+ return mapper(dbSession).selectOrderedByCeTaskUuid(ceTaskUuid);
+ }
+
public void insert(DbSession dbSession, WebhookDeliveryDto dto) {
mapper(dbSession).insert(dto);
}
*/
package org.sonar.db.webhook;
+import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
@CheckForNull
WebhookDeliveryDto selectByUuid(@Param("uuid") String uuid);
+ List<WebhookDeliveryLiteDto> selectOrderedByComponentUuid(@Param("componentUuid") String componentUuid);
+
+ List<WebhookDeliveryLiteDto> selectOrderedByCeTaskUuid(@Param("ceTaskUuid") String ceTaskUuid);
+
void insert(WebhookDeliveryDto dto);
void deleteComponentBeforeDate(@Param("componentUuid") String componentUuid, @Param("beforeDate") long beforeDate);
<mapper namespace="org.sonar.db.webhook.WebhookDeliveryMapper">
- <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDeliveryDto">
- select
+ <sql id="sqlLiteColumns">
uuid,
component_uuid as componentUuid,
ce_task_uuid as ceTaskUuid,
success,
http_status as httpStatus,
duration_ms as durationMs,
- payload,
- error_stacktrace as errorStacktrace,
created_at as createdAt
+ </sql>
+
+ <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDeliveryDto">
+ select
+ <include refid="sqlLiteColumns" />,
+ payload,
+ error_stacktrace as errorStacktrace
from webhook_deliveries
where uuid = #{uuid,jdbcType=VARCHAR}
</select>
+ <select id="selectOrderedByComponentUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDeliveryLiteDto">
+ select <include refid="sqlLiteColumns" />
+ from webhook_deliveries
+ where component_uuid = #{componentUuid,jdbcType=VARCHAR}
+ order by created_at desc
+ </select>
+
+ <select id="selectOrderedByCeTaskUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDeliveryLiteDto">
+ select <include refid="sqlLiteColumns" />
+ from webhook_deliveries
+ where ce_task_uuid = #{ceTaskUuid,jdbcType=VARCHAR}
+ order by created_at desc
+ </select>
+
<insert id="insert" parameterType="org.sonar.db.webhook.WebhookDeliveryDto" useGeneratedKeys="false">
insert into webhook_deliveries (
uuid,
public class WebhookDeliveryDaoTest {
- private static final long DATE_1 = 1_999_000L;
+ private static final long NOW = 1_500_000_000L;
+ private static final long BEFORE = NOW - 1_000L;
@Rule
public final DbTester dbTester = DbTester.create(System2.INSTANCE).setDisableDefaultOrganization(true);
assertThat(underTest.selectByUuid(dbSession, "missing")).isEmpty();
}
+ @Test
+ public void selectOrderedByComponentUuid_returns_empty_if_no_records() {
+ underTest.insert(dbSession, newDto("D1", "COMPONENT_1", "TASK_1"));
+
+ List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByComponentUuid(dbSession, "ANOTHER_COMPONENT");
+
+ assertThat(deliveries).isEmpty();
+ }
+
+ @Test
+ public void selectOrderedByComponentUuid_returns_records_ordered_by_date() {
+ WebhookDeliveryDto dto1 = newDto("D1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
+ WebhookDeliveryDto dto2 = newDto("D2", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto3 = newDto("D3", "COMPONENT_2", "TASK_1").setCreatedAt(NOW);
+ underTest.insert(dbSession, dto3);
+ underTest.insert(dbSession, dto2);
+ underTest.insert(dbSession, dto1);
+
+ List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByComponentUuid(dbSession, "COMPONENT_1");
+
+ assertThat(deliveries).extracting(WebhookDeliveryLiteDto::getUuid).containsExactly("D2", "D1");
+ }
+
+ @Test
+ public void selectOrderedByCeTaskUuid_returns_empty_if_no_records() {
+ underTest.insert(dbSession, newDto("D1", "COMPONENT_1", "TASK_1"));
+
+ List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByCeTaskUuid(dbSession, "ANOTHER_TASK");
+
+ assertThat(deliveries).isEmpty();
+ }
+
+ @Test
+ public void selectOrderedByCeTaskUuid_returns_records_ordered_by_date() {
+ WebhookDeliveryDto dto1 = newDto("D1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
+ WebhookDeliveryDto dto2 = newDto("D2", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto3 = newDto("D3", "COMPONENT_2", "TASK_2").setCreatedAt(NOW);
+ underTest.insert(dbSession, dto3);
+ underTest.insert(dbSession, dto2);
+ underTest.insert(dbSession, dto1);
+
+ List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByCeTaskUuid(dbSession, "TASK_1");
+
+ assertThat(deliveries).extracting(WebhookDeliveryLiteDto::getUuid).containsExactly("D2", "D1");
+ }
+
@Test
public void insert_row_with_only_mandatory_columns() {
WebhookDeliveryDto dto = newDto("DELIVERY_1", "COMPONENT_1", "TASK_1")
import org.sonarqube.ws.client.setting.SettingsService;
import org.sonarqube.ws.client.system.SystemService;
import org.sonarqube.ws.client.usertoken.UserTokensService;
+import org.sonarqube.ws.client.webhook.WebhooksService;
/**
* This class is not public anymore since version 5.5. It is
private final ProjectLinksService projectLinksService;
private final SettingsService settingsService;
private final RootsService rootsService;
+ private final WebhooksService webhooksService;
DefaultWsClient(WsConnector wsConnector) {
this.wsConnector = wsConnector;
this.projectLinksService = new ProjectLinksService(wsConnector);
this.settingsService = new SettingsService(wsConnector);
this.rootsService = new RootsService(wsConnector);
+ this.webhooksService = new WebhooksService(wsConnector);
}
@Override
public RootsService rootService() {
return rootsService;
}
+
+ @Override
+ public WebhooksService webhooks() {
+ return webhooksService;
+ }
}
import org.sonarqube.ws.client.setting.SettingsService;
import org.sonarqube.ws.client.system.SystemService;
import org.sonarqube.ws.client.usertoken.UserTokensService;
+import org.sonarqube.ws.client.webhook.WebhooksService;
/**
* Allows to request the web services of SonarQube server. Instance is provided by
* @since 6.2
*/
RootsService rootService();
+
+ /**
+ * @since 6.2
+ */
+ WebhooksService webhooks();
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ws.client.webhook;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class DeliveriesRequest {
+
+ private final String componentKey;
+ private final String ceTaskId;
+
+ private DeliveriesRequest(Builder builder) {
+ this.componentKey = builder.componentKey;
+ this.ceTaskId = builder.ceTaskId;
+ }
+
+ @CheckForNull
+ public String getComponentKey() {
+ return componentKey;
+ }
+
+ @CheckForNull
+ public String getCeTaskId() {
+ return ceTaskId;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String componentKey;
+ private String ceTaskId;
+
+ /**
+ * @see #builder()
+ */
+ private Builder() {
+ }
+
+ public Builder setComponentKey(@Nullable String s) {
+ this.componentKey = s;
+ return this;
+ }
+
+ public Builder setCeTaskId(@Nullable String s) {
+ this.ceTaskId = s;
+ return this;
+ }
+
+ public DeliveriesRequest build() {
+ return new DeliveriesRequest(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ws.client.webhook;
+
+import org.sonarqube.ws.Webhooks;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsConnector;
+
+public class WebhooksService extends BaseService {
+
+ public WebhooksService(WsConnector wsConnector) {
+ super(wsConnector, "api/webhooks");
+ }
+
+ /**
+ * @throws org.sonarqube.ws.client.HttpException if HTTP status code is not 2xx.
+ */
+ public Webhooks.DeliveriesWsResponse deliveries(DeliveriesRequest request) {
+ GetRequest httpRequest = new GetRequest(path("deliveries"))
+ .setParam("componentKey", request.getComponentKey())
+ .setParam("ceTaskId", request.getCeTaskId());
+ return call(httpRequest, Webhooks.DeliveriesWsResponse.parser());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.ws.client.webhook;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2016 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// SonarQube is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+syntax = "proto2";
+
+package sonarqube.ws.webhooks;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "Webhooks";
+option optimize_for = SPEED;
+
+// WS api/webhooks/deliveries
+message DeliveriesWsResponse {
+ repeated Delivery deliveries = 1;
+}
+
+message Delivery {
+ optional string id = 1;
+ optional string componentKey = 2;
+ optional string ceTaskId = 3;
+ optional string name = 4;
+ optional string url = 5;
+ optional string at = 6;
+ optional bool success = 7;
+ optional int32 httpStatus = 8;
+ optional int32 durationMs = 9;
+}