diff options
Diffstat (limited to 'src/main')
8 files changed, 258 insertions, 4 deletions
diff --git a/src/main/java/eu/ortlepp/notificationsender/NotificationHandler.java b/src/main/java/eu/ortlepp/notificationsender/NotificationHandler.java index 43e1aa9..17b4a46 100644 --- a/src/main/java/eu/ortlepp/notificationsender/NotificationHandler.java +++ b/src/main/java/eu/ortlepp/notificationsender/NotificationHandler.java @@ -1,12 +1,97 @@ package eu.ortlepp.notificationsender; import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.ortlepp.notificationsender.model.Notifications; +import eu.ortlepp.notificationsender.model.Response; +import eu.ortlepp.notificationsender.model.Status; +import eu.ortlepp.notificationsender.service.NotificationSender; +import eu.ortlepp.notificationsender.service.SnsNotificationSender; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; -public class NotificationHandler implements RequestHandler<String, String> { +/** + * AWS Lambda implementation. + */ +public class NotificationHandler implements RequestHandler<Map<String, Object>, Response> { - public String handleRequest(String event, Context context) { - return event.toLowerCase(); + private LambdaLogger logger; + + private NotificationSender sender; + + + /** + * Standard constructor for AWS runtime. Uses default NotificationSender implementation. + */ + public NotificationHandler() { + this.sender = null; + } + + + /** + * Constructor with custom NotificationSender implementation. Mainly for testing purposes. + * + * @param sender A custom NotificationSender + */ + public NotificationHandler(final NotificationSender sender) { + this.sender = sender; + } + + + /** + * Constructor with custom NotificationSender and Context implementation. + * Mainly for testing purposes. + * + * @param sender A custom NotificationSender + * @param context A custom Context (to initialize the build-in logger) + */ + public NotificationHandler(final NotificationSender sender, final Context context) { + this.sender = sender; + this.logger = context.getLogger(); + } + + + /** + * The AWS Lambda handler; executes the Lambda functionality. + * + * @param event The incoming event which triggered the Lambda + * @param context The context of the Lambda execution + * @return The final status of the execution + */ + public Response handleRequest(final Map<String, Object> event, final Context context) { + logger = context.getLogger(); + if (sender == null) { + sender = new SnsNotificationSender(logger); + } + + List<String> notifications = extractNotifications(event.get("body")); + if (!notifications.isEmpty() && sender.sendNotifications(notifications)) { + return new Response(Status.SUCCESS); + } + return new Response(Status.FAILED); + } + + + /** + * Extract the notifications from the input JSON. + * + * @param body The input JSON + * @return The extracted messages; empty list if an error occurred while parsing the JSON + */ + protected List<String> extractNotifications(final Object body) { + try { + Notifications notifications = + new ObjectMapper().readValue(body.toString(), Notifications.class); + return Arrays.asList(notifications.messages()); + } catch (JsonProcessingException ex) { + logger.log("parsing input JSON failed : " + ex.getMessage()); + return new ArrayList<>(); + } } -}
\ No newline at end of file +} diff --git a/src/main/java/eu/ortlepp/notificationsender/model/Notifications.java b/src/main/java/eu/ortlepp/notificationsender/model/Notifications.java new file mode 100644 index 0000000..65f7620 --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/model/Notifications.java @@ -0,0 +1,8 @@ +package eu.ortlepp.notificationsender.model; + +/** + * The expected input data of the Lambda. + * + * @param messages The messages / notifications + */ +public record Notifications(String[] messages) {} diff --git a/src/main/java/eu/ortlepp/notificationsender/model/Response.java b/src/main/java/eu/ortlepp/notificationsender/model/Response.java new file mode 100644 index 0000000..d931bed --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/model/Response.java @@ -0,0 +1,8 @@ +package eu.ortlepp.notificationsender.model; + +/** + * The response of a Lambda execution. + * + * @param status The final execution status of the Lambda + */ +public record Response(Status status) {} diff --git a/src/main/java/eu/ortlepp/notificationsender/model/Status.java b/src/main/java/eu/ortlepp/notificationsender/model/Status.java new file mode 100644 index 0000000..caeb0cd --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/model/Status.java @@ -0,0 +1,9 @@ +package eu.ortlepp.notificationsender.model; + +/** + * The status of a Lambda execution, shown in the response. + */ +public enum Status { + SUCCESS, + FAILED +} diff --git a/src/main/java/eu/ortlepp/notificationsender/service/NotificationSender.java b/src/main/java/eu/ortlepp/notificationsender/service/NotificationSender.java new file mode 100644 index 0000000..8c39c72 --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/service/NotificationSender.java @@ -0,0 +1,41 @@ +package eu.ortlepp.notificationsender.service; + +import java.util.List; + +/** + * Interface for notification senders. + */ +public interface NotificationSender { + + /** + * Send a list of notifications. + * + * @param notifications The notifications to send + * @return The result of the sending; true = successful, false = error occurred + */ + boolean sendNotifications(final List<String> notifications); + + + /** + * Format notifications for sending. + * If only one notification is passed, it is returned as is. + * If multiple notifications are passed, a numbered list of notifications is returned. + * + * @param notifications The notifications to format + * @return The formatted notifications + */ + default String formatNotifications(final List<String> notifications) { + if (notifications.size() == 1) { + return notifications.getFirst() + "\n\n"; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < notifications.size(); i++) { + builder.append(i + 1) + .append(". Notification:\n") + .append(notifications.get(i)) + .append("\n\n"); + } + return builder.toString(); + } + +} diff --git a/src/main/java/eu/ortlepp/notificationsender/service/SnsNotificationSender.java b/src/main/java/eu/ortlepp/notificationsender/service/SnsNotificationSender.java new file mode 100644 index 0000000..958365a --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/service/SnsNotificationSender.java @@ -0,0 +1,63 @@ +package eu.ortlepp.notificationsender.service; + +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import eu.ortlepp.notificationsender.util.Config; +import java.util.List; +import software.amazon.awssdk.http.HttpStatusCode; +import software.amazon.awssdk.services.sns.SnsClient; +import software.amazon.awssdk.services.sns.model.PublishRequest; +import software.amazon.awssdk.services.sns.model.PublishResponse; +import software.amazon.awssdk.services.sns.model.SnsException; + +/** + * A notification sender for Amazon SNS. + */ +public class SnsNotificationSender implements NotificationSender { + + private final LambdaLogger logger; + + + /** + * Constructor to initialize the sender. + * + * @param logger The logger of the AWS Lambda + */ + public SnsNotificationSender(final LambdaLogger logger) { + this.logger = logger; + } + + + /** + * Send a list of notifications to the configured SNS topic. + * All notifications are combined into a single message. + * + * @param notifications The notifications to send + * @return The result of the sending; true = successful, false = error occurred + */ + @Override + public boolean sendNotifications(final List<String> notifications) { + String notification = formatNotifications(notifications); + + try (SnsClient snsClient = SnsClient.builder().region(Config.REGION).build()) { + PublishRequest request = + PublishRequest.builder().message(notification).topicArn(Config.ARN).build(); + PublishResponse result = snsClient.publish(request); + int resultStatusCode = result.sdkHttpResponse().statusCode(); + + if (resultStatusCode == HttpStatusCode.OK) { + logger.log("notification sent successfully"); + return true; + } + + logger.log("sending notification failed with status " + resultStatusCode); + logger.log("failed message was " + notification); + return false; + + } catch (SnsException ex) { + logger.log("sending notification failed: " + ex.getMessage()); + logger.log("failed message was " + notification); + return false; + } + } + +} diff --git a/src/main/java/eu/ortlepp/notificationsender/util/Config.java b/src/main/java/eu/ortlepp/notificationsender/util/Config.java new file mode 100644 index 0000000..139ea24 --- /dev/null +++ b/src/main/java/eu/ortlepp/notificationsender/util/Config.java @@ -0,0 +1,40 @@ +package eu.ortlepp.notificationsender.util; + +import software.amazon.awssdk.regions.Region; + +/** + * Required configuration values read from environment variables. + */ +public final class Config { + + /** + * The region of the SNS topic. The Name of the environment variable is REGION. + * Value example: eu-central-1 + */ + public static final Region REGION; + + /** + * The ARN of the SNS topic. The Name of the environment variable is ARN. + * Value example: arn:aws:sns:eu-central-1:123456789:Topic-Name + */ + public static final String ARN; + + + static { + REGION = Region.of(getEnvVar("REGION")); + ARN = getEnvVar("ARN"); + } + + + private static String getEnvVar(final String key) { + String value = System.getenv(key); + if (value == null) { + throw new RuntimeException("environment variable not set: " + key); + } + return value; + } + + + private Config() {} + +} diff --git a/src/main/resources/.gitkeep b/src/main/resources/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/main/resources/.gitkeep |