private WebhookDeliveryDto toDto(WebhookDelivery delivery) { WebhookDeliveryDto dto = new WebhookDeliveryDto(); dto.setUuid(uuidFactory.create()); dto.setWebhookUuid(delivery.getWebhook().getUuid()); dto.setComponentUuid(delivery.getWebhook().getComponentUuid()); delivery.getWebhook().getCeTaskUuid().ifPresent(dto::setCeTaskUuid); delivery.getWebhook().getAnalysisUuid().ifPresent(dto::setAnalysisUuid); dto.setName(delivery.getWebhook().getName()); dto.setUrl(delivery.getEffectiveUrl().orElse(delivery.getWebhook().getUrl())); dto.setSuccess(delivery.isSuccess()); dto.setHttpStatus(delivery.getHttpStatus().orElse(null)); dto.setDurationMs(delivery.getDurationInMs().orElse(null)); dto.setErrorStacktrace(delivery.getError().map(Throwables::getStackTraceAsString).orElse(null)); dto.setPayload(delivery.getPayload().getJson()); dto.setCreatedAt(delivery.getAt()); return dto; } }
private static void log(WebhookDelivery delivery) { Optional<String> error = delivery.getErrorMessage(); if (error.isPresent()) { LOGGER.debug("Failed to send webhook '{}' | url={} | message={}", delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), error.get()); } else { LOGGER.debug("Sent webhook '{}' | url={} | time={}ms | status={}", delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), delivery.getDurationInMs().orElse(-1), delivery.getHttpStatus().orElse(-1)); } }
public WebhookDelivery build() { return new WebhookDelivery(this); } }
@Test public void isSuccess_returns_false_if_failed_to_send_http_request() { WebhookDelivery delivery = newBuilderTemplate() .setError(new IOException("Fail to connect")) .build(); assertThat(delivery.isSuccess()).isFalse(); }
@Test public void getErrorMessage_returns_root_cause_message_if_error() { Exception rootCause = new IOException("fail to connect"); Exception cause = new IOException("nested", rootCause); WebhookDelivery delivery = newBuilderTemplate() .setError(cause) .build(); assertThat(delivery.getErrorMessage().get()).isEqualTo("fail to connect"); }
@Test public void redirects_throws_ISE_if_header_Location_is_missing() { HttpUrl url = server.url("/redirect"); Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString()); server.enqueue(new MockResponse().setResponseCode(307)); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); Throwable error = delivery.getError().get(); assertThat(error) .isInstanceOf(IllegalStateException.class) .hasMessage("Missing HTTP header 'Location' in redirect of " + url); }
@Test public void send_basic_authentication_header_if_url_contains_credentials() throws Exception { HttpUrl url = server.url("/ping").newBuilder().username("theLogin").password("thePassword").build(); Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString()); server.enqueue(new MockResponse().setBody("pong")); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getWebhook().getUrl()) .isEqualTo(url.toString()) .contains("://theLogin:thePassword@"); RecordedRequest recordedRequest = takeAndVerifyPostRequest("/ping"); assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password())); }
@Test public void credentials_are_propagated_to_POST_redirects() throws Exception { HttpUrl url = server.url("/redirect").newBuilder().username("theLogin").password("thePassword").build(); Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString()); // /redirect redirects to /target server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target"))); server.enqueue(new MockResponse().setResponseCode(200)); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getHttpStatus().get()).isEqualTo(200); RecordedRequest redirectedRequest = takeAndVerifyPostRequest("/redirect"); assertThat(redirectedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password())); RecordedRequest targetRequest = takeAndVerifyPostRequest("/target"); assertThat(targetRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password())); }
@Test public void isSuccess_returns_true_if_http_response_returns_2xx_code() { WebhookDelivery delivery = newBuilderTemplate() .setHttpStatus(204) .build(); assertThat(delivery.isSuccess()).isTrue(); }
@Test public void getErrorMessage_returns_empty_if_no_error() { WebhookDelivery delivery = newBuilderTemplate().build(); assertThat(delivery.getErrorMessage()).isEmpty(); }
@Test public void redirects_throws_ISE_if_header_Location_does_not_relate_to_a_supported_protocol() { HttpUrl url = server.url("/redirect"); Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString()); server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", "ftp://foo")); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); Throwable error = delivery.getError().get(); assertThat(error) .isInstanceOf(IllegalStateException.class) .hasMessage("Unsupported protocol in redirect of " + url + " to ftp://foo"); }
@Test public void silently_catch_error_when_url_is_incorrect() { Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", "this_is_not_an_url"); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getHttpStatus()).isEmpty(); assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0); assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class); assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url"); assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getWebhook()).isSameAs(webhook); assertThat(delivery.getPayload()).isSameAs(PAYLOAD); }
private static void log(WebhookDelivery delivery) { Optional<String> error = delivery.getErrorMessage(); if (error.isPresent()) { LOGGER.debug("Failed to send webhook '{}' | url={} | message={}", delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), error.get()); } else { LOGGER.debug("Sent webhook '{}' | url={} | time={}ms | status={}", delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), delivery.getDurationInMs().orElse(-1), delivery.getHttpStatus().orElse(-1)); } }
@Test public void isSuccess_returns_false_if_http_response_returns_error_status() { WebhookDelivery delivery = newBuilderTemplate() .setHttpStatus(404) .build(); assertThat(delivery.isSuccess()).isFalse(); }
public WebhookDelivery build() { return new WebhookDelivery(this); } }
@Test public void silently_catch_error_when_external_server_does_not_answer() throws Exception { Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString()); server.shutdown(); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getHttpStatus()).isEmpty(); assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0); // message can be "Connection refused" or "connect timed out" assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)"); assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getWebhook()).isSameAs(webhook); assertThat(delivery.getPayload()).isSameAs(PAYLOAD); }
@Test public void persist_generates_uuid_then_inserts_record() { when(uuidFactory.create()).thenReturn(DELIVERY_UUID); WebhookDelivery delivery = newBuilderTemplate().build(); underTest.persist(delivery); WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get(); assertThat(dto.getUuid()).isEqualTo(DELIVERY_UUID); assertThat(dto.getWebhookUuid()).isEqualTo("WEBHOOK_UUID_1"); assertThat(dto.getComponentUuid()).isEqualTo(delivery.getWebhook().getComponentUuid()); assertThat(dto.getCeTaskUuid()).isEqualTo(delivery.getWebhook().getCeTaskUuid().get()); assertThat(dto.getName()).isEqualTo(delivery.getWebhook().getName()); assertThat(dto.getUrl()).isEqualTo(delivery.getWebhook().getUrl()); assertThat(dto.getCreatedAt()).isEqualTo(delivery.getAt()); assertThat(dto.getHttpStatus()).isEqualTo(delivery.getHttpStatus().get()); assertThat(dto.getDurationMs()).isEqualTo(delivery.getDurationInMs().get()); assertThat(dto.getPayload()).isEqualTo(delivery.getPayload().getJson()); assertThat(dto.getErrorStacktrace()).isNull(); }
/** * SONAR-8799 */ @Test public void redirects_should_be_followed_with_POST_method() throws Exception { Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/redirect").toString()); // /redirect redirects to /target server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target"))); server.enqueue(new MockResponse().setResponseCode(200)); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getHttpStatus().get()).isEqualTo(200); assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0); assertThat(delivery.getError()).isEmpty(); assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getWebhook()).isSameAs(webhook); assertThat(delivery.getPayload()).isSameAs(PAYLOAD); takeAndVerifyPostRequest("/redirect"); takeAndVerifyPostRequest("/target"); }
@Test public void send_posts_payload_to_http_server() throws Exception { Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString()); server.enqueue(new MockResponse().setBody("pong").setResponseCode(201)); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); assertThat(delivery.getHttpStatus()).hasValue(201); assertThat(delivery.getWebhook().getUuid()).isEqualTo(WEBHOOK_UUID); assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0); assertThat(delivery.getError()).isEmpty(); assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getWebhook()).isSameAs(webhook); assertThat(delivery.getPayload()).isSameAs(PAYLOAD); RecordedRequest recordedRequest = server.takeRequest(); assertThat(recordedRequest.getMethod()).isEqualTo("POST"); assertThat(recordedRequest.getPath()).isEqualTo("/ping"); assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(PAYLOAD.getJson()); assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2"); assertThat(recordedRequest.getHeader("Content-Type")).isEqualTo("application/json; charset=utf-8"); assertThat(recordedRequest.getHeader("X-SonarQube-Project")).isEqualTo(PAYLOAD.getProjectKey()); }
private WebhookDeliveryDto toDto(WebhookDelivery delivery) { WebhookDeliveryDto dto = new WebhookDeliveryDto(); dto.setUuid(uuidFactory.create()); dto.setWebhookUuid(delivery.getWebhook().getUuid()); dto.setComponentUuid(delivery.getWebhook().getComponentUuid()); delivery.getWebhook().getCeTaskUuid().ifPresent(dto::setCeTaskUuid); delivery.getWebhook().getAnalysisUuid().ifPresent(dto::setAnalysisUuid); dto.setName(delivery.getWebhook().getName()); dto.setUrl(delivery.getWebhook().getUrl()); dto.setSuccess(delivery.isSuccess()); dto.setHttpStatus(delivery.getHttpStatus().orElse(null)); dto.setDurationMs(delivery.getDurationInMs().orElse(null)); dto.setErrorStacktrace(delivery.getError().map(Throwables::getStackTraceAsString).orElse(null)); dto.setPayload(delivery.getPayload().getJson()); dto.setCreatedAt(delivery.getAt()); return dto; } }