From 21b4be2173e36fef157e46582060e265edb1bf45 Mon Sep 17 00:00:00 2001 From: Guillaume Jambet Date: Wed, 31 Jan 2018 18:47:59 +0100 Subject: [PATCH] SONAR-10344 api/webhooks/search returns webhooks for global and project --- .../sonar/server/setting/ws/ValuesAction.java | 4 +- .../sonar/server/webhook/WebHooksImpl.java | 4 +- .../sonar/server/webhook/ws/SearchAction.java | 90 +++++++-- .../server/webhook/ws/WebhookSearchDTO.java | 26 --- .../webhook/ws/WebhooksWsParameters.java | 25 ++- .../webhook/ws/example-webhooks-search.json | 18 +- .../server/webhook/ws/SearchActionTest.java | 176 +++++++++++++++++- server/sonar-web/src/main/js/api/webhooks.ts | 16 +- server/sonar-web/src/main/js/app/types.ts | 15 ++ .../main/js/apps/webhooks/components/App.tsx | 6 +- .../apps/webhooks/components/WebhookItem.tsx | 2 +- .../apps/webhooks/components/WebhooksList.tsx | 15 +- sonar-ws/src/main/protobuf/ws-webhooks.proto | 30 +-- 13 files changed, 309 insertions(+), 118 deletions(-) delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSearchDTO.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java index 4a947af76ae..57fefaeb3a0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java @@ -145,8 +145,8 @@ public class ValuesAction implements SettingsWsAction { private Set loadKeys(ValuesRequest valuesRequest) { List keys = valuesRequest.getKeys(); - return keys == null || keys.isEmpty() ? concat(propertyDefinitions.getAll().stream().map(PropertyDefinition::key), SERVER_SETTING_KEYS.stream()).collect(Collectors.toSet()) - : ImmutableSet.copyOf(keys); + return keys == null || keys.isEmpty() ? concat(propertyDefinitions.getAll().stream().map(PropertyDefinition::key), + SERVER_SETTING_KEYS.stream()).collect(Collectors.toSet()) : ImmutableSet.copyOf(keys); } private Optional loadComponent(DbSession dbSession, ValuesRequest valuesRequest) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java index fdcef061f7e..cae1aa5eaf0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java @@ -57,7 +57,7 @@ public class WebHooksImpl implements WebHooks { .isPresent(); } - private static Stream readWebHooksFrom(Configuration config) { + public static Stream readWebHooksFrom(Configuration config) { return Stream.concat( getWebhookProperties(config, WebhookProperties.GLOBAL_KEY).stream(), getWebhookProperties(config, WebhookProperties.PROJECT_KEY).stream()) @@ -110,7 +110,7 @@ public class WebHooksImpl implements WebHooks { } } - private static final class NameUrl { + public static final class NameUrl { private final String name; private final String url; diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/SearchAction.java index c31b49b92e3..93e0c23e0f4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/SearchAction.java @@ -1,36 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.base.Optional; +import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +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.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.setting.ws.Setting; +import org.sonar.server.setting.ws.SettingsFinder; import org.sonar.server.user.UserSession; -import org.sonarqube.ws.Webhooks; +import org.sonarqube.ws.Webhooks.SearchWsResponse.Builder; +import static org.apache.commons.lang.StringUtils.isNotBlank; +import static org.sonar.api.web.UserRole.ADMIN; import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.SEARCH_ACTION; import static org.sonar.server.ws.KeyExamples.KEY_ORG_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.ws.WsUtils.writeProtobuf; +import static org.sonarqube.ws.Webhooks.SearchWsResponse.newBuilder; public class SearchAction implements WebhooksWsAction { private final DbClient dbClient; private final UserSession userSession; + private final SettingsFinder settingsFinder; - public SearchAction(DbClient dbClient, UserSession userSession) { + public SearchAction(DbClient dbClient, UserSession userSession, SettingsFinder settingsFinder) { this.dbClient = dbClient; this.userSession = userSession; + this.settingsFinder = settingsFinder; } @Override public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction(SEARCH_ACTION) - .setDescription("Search for webhooks associated to an organization or a project.
") + .setDescription("Search for global or project webhooks") .setSince("7.1") .setResponseExample(Resources.getResource(this.getClass(), "example-webhooks-search.json")) .setHandler(this); @@ -51,25 +85,45 @@ public class SearchAction implements WebhooksWsAction { @Override public void handle(Request request, Response response) throws Exception { - Webhooks.SearchWsResponse.Builder searchResponse = Webhooks.SearchWsResponse.newBuilder(); + String projectKey = request.param(PROJECT_KEY_PARAM); - // FIXME : hard coded to test plumbing - ArrayList webhookSearchDTOS = new ArrayList<>(); - webhookSearchDTOS.add(new WebhookSearchDTO("UUID-1", "my first webhook", "http://www.my-webhook-listener.com/sonarqube")); - webhookSearchDTOS.add(new WebhookSearchDTO("UUID-2", "my 2nd webhook", "https://www.my-other-webhook-listener.com/fancy-listner")); + userSession.checkLoggedIn(); - for (WebhookSearchDTO dto : webhookSearchDTOS) { - searchResponse.addWebhooksBuilder() - .setKey(dto.getKey()) - .setName(dto.getName()) - .setUrl(dto.getUrl()); + writeResponse(request, response, doHandle(projectKey)); + + } + + private List doHandle(@Nullable String projectKey) { + + try (DbSession dbSession = dbClient.openSession(true)) { + + if (isNotBlank(projectKey)) { + Optional component = dbClient.componentDao().selectByKey(dbSession, projectKey); + checkFoundWithOptional(component, "project %s does not exist", projectKey); + userSession.checkComponentPermission(ADMIN, component.get()); + return new ArrayList<>(settingsFinder.loadComponentSettings(dbSession, + ImmutableSet.of("sonar.webhooks.project"), component.get()).get(component.get().uuid())); + } else { + userSession.checkIsSystemAdministrator(); + return settingsFinder.loadGlobalSettings(dbSession, ImmutableSet.of("sonar.webhooks.global")); + } } + } + + private static void writeResponse(Request request, Response response, List settings) { + + Builder responseBuilder = newBuilder(); - writeProtobuf(searchResponse.build(), request, response); + settings + .stream() + .map(Setting::getPropertySets) + .flatMap(Collection::stream) + .forEach(map -> responseBuilder.addWebhooksBuilder() + .setKey("") + .setName(map.get("name")) + .setUrl(map.get("url"))); + + writeProtobuf(responseBuilder.build(), request, response); } } - -// {"key":"UUID-1","name":,"url":,"latestDelivery":{"id":"d1","at":"2017-07-14T04:40:00+0200","success":true,"httpStatus":200,"durationMs":10}}, -// {"key":"UUID-2","name":"my 2nd -// webhook","url":"https://www.my-other-webhook-listener.com/fancy-listner","latestDelivery":{"id":"d2","at":"2017-07-14T04:40:00+0200","success":true,"httpStatus":200,"durationMs":10}} diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSearchDTO.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSearchDTO.java deleted file mode 100644 index 617ef66fec0..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSearchDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.sonar.server.webhook.ws; - -public class WebhookSearchDTO { - - private final String key; - private final String name; - private final String url; - - public WebhookSearchDTO(String key, String name, String url) { - this.key = key; - this.name = name; - this.url = url; - } - - public String getKey() { - return key; - } - - public String getName() { - return name; - } - - public String getUrl() { - return url; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java index 3a8d18acf66..7430e3f2f1d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java @@ -1,14 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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; class WebhooksWsParameters { static final String WEBHOOKS_CONTROLLER = "api/webhooks"; - static final String SEARCH_ACTION = "search"; - static final String ORGANIZATION_KEY_PARAM = "organization"; static final String PROJECT_KEY_PARAM = "project"; + private WebhooksWsParameters() { + // hiding constructor + } + } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-webhooks-search.json b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-webhooks-search.json index 0f5d3befe28..ba3ba250df4 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-webhooks-search.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-webhooks-search.json @@ -3,26 +3,12 @@ { "key": "UUID-1", "name": "my first webhook", - "url": "http://www.my-webhook-listener.com/sonarqube", - "latestDelivery": { - "id": "d1", - "at": "2017-07-14T04:40:00+0200", - "success": true, - "httpStatus": 200, - "durationMs": 10 - } + "url": "http://www.my-webhook-listener.com/sonarqube" }, { "key": "UUID-2", "name": "my 2nd webhook", - "url": "https://www.my-other-webhook-listener.com/fancy-listner", - "latestDelivery": { - "id": "d2", - "at": "2017-07-14T04:40:00+0200", - "success": true, - "httpStatus": 200, - "durationMs": 10 - } + "url": "https://www.my-other-webhook-listener.com/fancy-listner" } ] } \ No newline at end of file diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/SearchActionTest.java index 68264390abd..d2c7606e39d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/SearchActionTest.java @@ -1,35 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.server.ws.WebService; +import org.sonar.api.server.ws.WebService.Param; import org.sonar.db.DbClient; -import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.property.PropertyDbTester; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.setting.ws.SettingsFinder; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Webhooks.SearchWsResponse; +import org.sonarqube.ws.Webhooks.SearchWsResponse.Search; +import static com.google.common.collect.ImmutableMap.of; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.junit.rules.ExpectedException.none; +import static org.sonar.api.PropertyType.PROPERTY_SET; +import static org.sonar.api.config.PropertyFieldDefinition.build; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.db.DbTester.create; +import static org.sonar.server.tester.UserSessionRule.standalone; +import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM; public class SearchActionTest { @Rule - public ExpectedException expectedException = ExpectedException.none(); + public ExpectedException expectedException = none(); @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); + public UserSessionRule userSession = standalone(); @Rule - public DbTester db = DbTester.create(); + public DbTester db = create(); private DbClient dbClient = db.getDbClient(); - private DbSession dbSession = db.getSession(); - - private org.sonar.server.webhook.ws.SearchAction underTest = new SearchAction(dbClient, userSession); + private PropertyDefinitions definitions = new PropertyDefinitions(); + private SettingsFinder settingsFinder = new SettingsFinder(dbClient, definitions); + private SearchAction underTest = new SearchAction(dbClient, userSession, settingsFinder); private WsActionTester wsActionTester = new WsActionTester(underTest); + private ComponentDbTester componentDbTester = new ComponentDbTester(db); + + private PropertyDbTester propertyDb = new PropertyDbTester(db); + @Test public void definition() { @@ -40,10 +84,124 @@ public class SearchActionTest { assertThat(action.isPost()).isFalse(); assertThat(action.responseExampleAsString()).isNotEmpty(); assertThat(action.params()) - .extracting(WebService.Param::key, WebService.Param::isRequired) + .extracting(Param::key, Param::isRequired) .containsExactlyInAnyOrder( tuple("organization", false), tuple("project", false)); } -} \ No newline at end of file + @Test + public void search_global_webhooks() { + + definitions.addComponent(PropertyDefinition + .builder("sonar.webhooks.global") + .type(PROPERTY_SET) + .fields(asList( + build("name").name("name").build(), + build("url").name("url").build())) + .build()); + propertyDb.insertPropertySet("sonar.webhooks.global", null, + of("name", "my first global webhook", "url", "http://127.0.0.1/first-global"), + of("name", "my second global webhook", "url", "http://127.0.0.1/second-global")); + + userSession.logIn().setSystemAdministrator(); + + SearchWsResponse response = wsActionTester.newRequest() + .executeProtobuf(SearchWsResponse.class); + + assertThat(response.getWebhooksList()) + .extracting(Search::getName, Search::getUrl) + .containsExactly(tuple("my first global webhook", "http://127.0.0.1/first-global"), + tuple("my second global webhook", "http://127.0.0.1/second-global")); + } + + @Test + public void search_project_webhooks_when_no_organization_is_provided() { + OrganizationDto defaultOrganization = db.getDefaultOrganization(); + ComponentDto project = db.components().insertPublicProject(defaultOrganization); + + definitions.addComponent(PropertyDefinition + .builder("sonar.webhooks.global") + .type(PROPERTY_SET) + .fields(asList( + build("name").name("name").build(), + build("url").name("url").build())) + .build()); + propertyDb.insertPropertySet("sonar.webhooks.global", null, + of("name", "my first global webhook", "url", "http://127.0.0.1/first-global"), + of("name", "my second global webhook", "url", "http://127.0.0.1/second-global")); + + definitions.addComponent(PropertyDefinition + .builder("sonar.webhooks.project") + .type(PROPERTY_SET) + .fields(asList( + build("name").name("name").build(), + build("url").name("url").build())) + .build()); + propertyDb.insertPropertySet("sonar.webhooks.project", project, + of("name", "my first project webhook", "url", "http://127.0.0.1/first-project"), + of("name", "my second project webhook", "url", "http://127.0.0.1/second-project")); + + userSession.logIn().addProjectPermission(ADMIN, project); + + SearchWsResponse response = wsActionTester.newRequest() + .setParam(PROJECT_KEY_PARAM, project.getKey()) + .executeProtobuf(SearchWsResponse.class); + + assertThat(response.getWebhooksList()) + .extracting(Search::getName, Search::getUrl) + .containsExactly(tuple("my first project webhook", "http://127.0.0.1/first-project"), + tuple("my second project webhook", "http://127.0.0.1/second-project")); + + } + + @Test + public void return_UnauthorizedException_if_not_logged_in() throws Exception { + + userSession.anonymous(); + expectedException.expect(UnauthorizedException.class); + + wsActionTester.newRequest() + .executeProtobuf(SearchWsResponse.class); + } + + @Test + public void return_NotFoundException_if_not_project_is_not_found() throws Exception { + + userSession.logIn().setSystemAdministrator(); + expectedException.expect(NotFoundException.class); + + wsActionTester.newRequest() + .setParam(PROJECT_KEY_PARAM, "pipo") + .executeProtobuf(SearchWsResponse.class); + } + + @Test + public void throw_ForbiddenException_if_not_organization_administrator() { + + userSession.logIn(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + wsActionTester.newRequest() + .executeProtobuf(SearchWsResponse.class); + } + + @Test + public void throw_ForbiddenException_if_not_project_administrator() { + + ComponentDto project = componentDbTester.insertPrivateProject(); + + userSession.logIn(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + wsActionTester.newRequest() + .setParam(PROJECT_KEY_PARAM, project.getKey()) + .executeProtobuf(SearchWsResponse.class); + + } + +} diff --git a/server/sonar-web/src/main/js/api/webhooks.ts b/server/sonar-web/src/main/js/api/webhooks.ts index fa526855e02..547702464b1 100644 --- a/server/sonar-web/src/main/js/api/webhooks.ts +++ b/server/sonar-web/src/main/js/api/webhooks.ts @@ -19,21 +19,7 @@ */ import { getJSON } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; - -export interface Delivery { - id: string; - at: string; - success: boolean; - httpStatus: number; - durationMs: number; -} - -export interface Webhook { - key: string; - name: string; - url: string; - latestDelivery?: Delivery; -} +import { Webhook } from '../app/types'; export function searchWebhooks(data: { organization: string | undefined; diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 13b8bac9f66..43f2c58bad5 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -383,3 +383,18 @@ export enum Visibility { Public = 'public', Private = 'private' } + +export interface Webhook { + key: string; + latestDelivery?: WebhookDelivery; + name: string; + url: string; +} + +export interface WebhookDelivery { + at: string; + durationMs: number; + httpStatus: number; + id: string; + success: boolean; +} diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx index 36f26f55913..e1d559ef307 100644 --- a/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx @@ -21,13 +21,13 @@ import * as React from 'react'; import { Helmet } from 'react-helmet'; import PageHeader from './PageHeader'; import WebhooksList from './WebhooksList'; -import { searchWebhooks, Webhook } from '../../../api/webhooks'; -import { LightComponent, Organization } from '../../../app/types'; +import { searchWebhooks } from '../../../api/webhooks'; +import { LightComponent, Organization, Webhook } from '../../../app/types'; import { translate } from '../../../helpers/l10n'; interface Props { - organization: Organization | undefined; component?: LightComponent; + organization: Organization | undefined; } interface State { diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx index 1be6a9da181..f156900d581 100644 --- a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx +++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Webhook } from '../../../api/webhooks'; +import { Webhook } from '../../../app/types'; interface Props { webhook: Webhook; diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx index 61689e75561..600fad4e8d3 100644 --- a/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx +++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import WebhookItem from './WebhookItem'; -import { Webhook } from '../../../api/webhooks'; +import { Webhook } from '../../../app/types'; import { translate } from '../../../helpers/l10n'; interface Props { @@ -36,21 +36,16 @@ export default class WebhooksList extends React.PureComponent { ); - renderNoWebhooks = () => ( - - {translate('webhooks.no_result')} - - ); - render() { const { webhooks } = this.props; + if (webhooks.length < 1) { + return

{translate('webhooks.no_result')}

; + } return ( {this.renderHeader()} - {webhooks.length > 0 - ? webhooks.map(webhook => ) - : this.renderNoWebhooks()} + {webhooks.map(webhook => )}
); diff --git a/sonar-ws/src/main/protobuf/ws-webhooks.proto b/sonar-ws/src/main/protobuf/ws-webhooks.proto index 30ae14d8a4d..357bc403028 100644 --- a/sonar-ws/src/main/protobuf/ws-webhooks.proto +++ b/sonar-ws/src/main/protobuf/ws-webhooks.proto @@ -24,27 +24,29 @@ option java_package = "org.sonarqube.ws"; option java_outer_classname = "Webhooks"; option optimize_for = SPEED; -// WS api/webhooks/search +// GET api/webhooks/search message SearchWsResponse { repeated Search webhooks = 1; -} -message Search { - optional string key = 1; - optional string name = 2; - optional string url = 3; - optional LatestDelivery latestDelivery = 4; -} + message Search { + optional string key = 1; + optional string name = 2; + optional string url = 3; + optional LatestDelivery latestDelivery = 4; -message LatestDelivery { - optional string id = 1; - optional string at = 2; - optional string success = 3; - optional string httpStatus = 4; - optional string durationMs = 5; + message LatestDelivery { + optional string id = 1; + optional string at = 2; + optional string success = 3; + optional string httpStatus = 4; + optional string durationMs = 5; + } + } } + + // WS api/webhooks/deliveries message DeliveriesWsResponse { repeated Delivery deliveries = 1; -- 2.39.5