feat: add notification channels for email and webhooks
This commit is contained in:
1 parent
959b400f5a
commit
aec31a4555
9 files changed
+162
No files matched your search
@@ -0,0 +1,23 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class EmailNotificationChannel implements NotificationChannel {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EmailNotificationChannel.class);
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(NotificationMessage message) {
|
||||
log.info("[email] user={} type={} resource='{}' [{} - {}] :: {}",
|
||||
message.recipientUserId(), message.type(), message.resourceName(),
|
||||
message.startTime(), message.endTime(), message.summary());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
public class HttpWebhookGateway implements WebhookGateway {
|
||||
|
||||
private final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(5))
|
||||
.build();
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public HttpWebhookGateway(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void post(String url, NotificationMessage message) {
|
||||
try {
|
||||
String body = objectMapper.writeValueAsString(message);
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body))
|
||||
.build();
|
||||
httpClient.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
} catch (Exception failure) {
|
||||
throw new WebhookDeliveryException(url, failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
public interface NotificationChannel {
|
||||
|
||||
String name();
|
||||
|
||||
void deliver(NotificationMessage message);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record NotificationMessage(
|
||||
NotificationType type,
|
||||
Long organizationId,
|
||||
Long recipientUserId,
|
||||
Long resourceId,
|
||||
String resourceName,
|
||||
Instant startTime,
|
||||
Instant endTime,
|
||||
String summary) {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class NotificationService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
|
||||
|
||||
private final List<NotificationChannel> channels;
|
||||
|
||||
public NotificationService(List<NotificationChannel> channels) {
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
public void dispatch(NotificationMessage message) {
|
||||
for (NotificationChannel channel : channels) {
|
||||
try {
|
||||
channel.deliver(message);
|
||||
} catch (RuntimeException failure) {
|
||||
log.warn("Notification channel '{}' failed to deliver {} for user {}",
|
||||
channel.name(), message.type(), message.recipientUserId(), failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
public enum NotificationType {
|
||||
RESERVATION_CREATED,
|
||||
RESERVATION_CANCELLED,
|
||||
WAITLIST_PROMOTED
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
public class WebhookDeliveryException extends RuntimeException {
|
||||
|
||||
public WebhookDeliveryException(String url, Throwable cause) {
|
||||
super("Failed to deliver webhook to " + url, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
public interface WebhookGateway {
|
||||
|
||||
void post(String url, NotificationMessage message);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.slotline.notification;
|
||||
|
||||
import com.slotline.config.SlotlineProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "slotline.notifications.webhook", name = "url")
|
||||
public class WebhookNotificationChannel implements NotificationChannel {
|
||||
|
||||
private final WebhookGateway gateway;
|
||||
private final String url;
|
||||
|
||||
public WebhookNotificationChannel(WebhookGateway gateway, SlotlineProperties properties) {
|
||||
this.gateway = gateway;
|
||||
this.url = properties.getNotifications().getWebhook().getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "webhook";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(NotificationMessage message) {
|
||||
gateway.post(url, message);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user