From 2610ee3790f8ce94ee0afea6f87c80bd43f78f35 Mon Sep 17 00:00:00 2001 From: Olivier Levitt Date: Fri, 27 Sep 2024 15:57:53 +0200 Subject: [PATCH] Add events webhook (#500) --- README.md | 10 +- .../api/events/WebhookEventListener.java | 99 +++++++++++++++++++ .../src/main/resources/application.properties | 8 ++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 onyxia-api/src/main/java/fr/insee/onyxia/api/events/WebhookEventListener.java diff --git a/README.md b/README.md index be8cff08..8844a871 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,13 @@ Configurable properties : | `http.proxyPassword` | | Password if the proxy requires authentication | ### Events -| Key | Default | Description | -| --------------------- |---------|----------------------------------------| -| `event.logging.enabled` | `true` | whether events should be logged or not | +| Key | Default | Description | +|--------------------------|--|--------------------------------------------------------------------| +| `event.logging.enabled` | `true` | whether events should be logged or not | +| `event.webhook.enabled` | `false` | whether events should be sent to an external webhook via HTTP POST | +| `event.webhook.url` | | URL of the webhook to send the events to | +| `event.webhook.includes` | | List of events types to send the webhook for (empty = all events). e.g `service.uninstall,service.install` | +| `event.webhook.excludes` | | List of events types to ignore for the webhook. e.g `service.uninstall,service.install` | ### Other configurations | Key | Default | Description | diff --git a/onyxia-api/src/main/java/fr/insee/onyxia/api/events/WebhookEventListener.java b/onyxia-api/src/main/java/fr/insee/onyxia/api/events/WebhookEventListener.java new file mode 100644 index 00000000..609e2436 --- /dev/null +++ b/onyxia-api/src/main/java/fr/insee/onyxia/api/events/WebhookEventListener.java @@ -0,0 +1,99 @@ +package fr.insee.onyxia.api.events; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.stereotype.Component; + +@EnableAsync +@Component +@ConditionalOnProperty(name = "event.webhook.enabled", havingValue = "true") +public class WebhookEventListener { + + private final ObjectMapper objectMapper; + + private static final MediaType JSON = MediaType.get("application/json"); + + private static final Logger LOGGER = LoggerFactory.getLogger(WebhookEventListener.class); + + @Value("${event.webhook.url}") + private String url; + + @Value("${event.webhook.includes}") + private List includes = new ArrayList<>(); + + @Value("${event.webhook.excludes}") + private List excludes = new ArrayList<>(); + + private final OkHttpClient httpClient; + + @Autowired + public WebhookEventListener(ObjectMapper objectMapper, OkHttpClient okHttpClient) { + this.objectMapper = objectMapper; + this.httpClient = okHttpClient; + } + + @Async + @EventListener + public void onOnyxiaEvent(OnyxiaEvent onyxiaEvent) throws JsonProcessingException { + if (!includes.isEmpty() && !includes.contains(onyxiaEvent.getType())) { + return; + } + if (!excludes.isEmpty() && excludes.contains(onyxiaEvent.getType())) { + return; + } + RequestBody body = RequestBody.create(objectMapper.writeValueAsString(onyxiaEvent), JSON); + Request request = new Request.Builder().url(url).post(body).build(); + try { + httpClient.newCall(request).execute(); + } catch (IOException e) { + LOGGER.warn("Failure while sending event to webhook, will not be retried", e); + } + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getIncludes() { + return includes; + } + + public void setIncludes(List includes) { + this.includes = includes; + } + + public List getExcludes() { + return excludes; + } + + public void setExcludes(List excludes) { + this.excludes = excludes; + } + + public OkHttpClient getHttpClient() { + return httpClient; + } +} diff --git a/onyxia-api/src/main/resources/application.properties b/onyxia-api/src/main/resources/application.properties index 36f11194..4a79c4d0 100644 --- a/onyxia-api/src/main/resources/application.properties +++ b/onyxia-api/src/main/resources/application.properties @@ -29,5 +29,13 @@ springdoc.swagger-ui.oauth.clientId= springdoc.swagger-ui.oauth.clientSecret= # Enable events logging event.logging.enabled=true +# Enable events webhook +event.webhook.enabled=false +# URL to send the events to +event.webhook.url= +# List of events types to send the webhook for (empty = all events). e.g service.uninstall,service.install +event.webhook.includes= +# List of events types to ignore for the webhook. e.g service.uninstall,service.install +event.webhook.excludes= # Response stream configuration spring.mvc.async.request-timeout=600000 \ No newline at end of file