Skip to content

Commit

Permalink
Merge pull request #8 from sailthru/LT-984-m-particle-outgoing-lambda…
Browse files Browse the repository at this point in the history
…-send-message-to-m-particle-endpoint

[LT-984] Send messages to mParticle
  • Loading branch information
jc-sailthru authored Jul 4, 2024
2 parents e9fca6c + 4e78fa3 commit d091223
Show file tree
Hide file tree
Showing 18 changed files with 572 additions and 17 deletions.
6 changes: 6 additions & 0 deletions .idea/jpa-buddy.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import com.sailthru.gradle.ProjectType

plugins {
id("java")
id("com.sailthru.gradle") version("v0.9.0")
id("com.sailthru.gradle") version ("v0.9.0")
}

sailthru {
type = ProjectType.LAMBDA
javaVersion = JavaVersion.VERSION_21
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}

dependencies {
implementation(platform("software.amazon.awssdk:bom:2.25.60"))
Expand All @@ -18,9 +23,14 @@ dependencies {
implementation("com.amazonaws:aws-lambda-java-core:1.2.1")
implementation("com.amazonaws:aws-lambda-java-events:3.11.0")
implementation("org.slf4j:slf4j-simple:1.7.32")
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.1")
implementation("com.mparticle:server-events-sdk:2.5.4")

testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.hamcrest:hamcrest:2.2")
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}

tasks.test {
Expand Down
44 changes: 35 additions & 9 deletions gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@
# This file is expected to be part of source control.
com.amazonaws:aws-lambda-java-core:1.2.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.amazonaws:aws-lambda-java-events:3.11.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=checkstyle
com.fasterxml.jackson.core:jackson-annotations:2.17.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.17.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.17.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.17.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=checkstyle,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.gson:gson:2.8.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
com.google.guava:failureaccess:1.0.1=checkstyle
com.google.guava:guava:31.0.1-jre=checkstyle
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=checkstyle
com.google.j2objc:j2objc-annotations:1.3=checkstyle
com.mparticle:server-events-sdk:2.5.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.puppycrawl.tools:checkstyle:9.3=checkstyle
com.squareup.okhttp3:okhttp:3.14.9=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio:1.17.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.retrofit2:converter-gson:2.9.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.retrofit2:converter-scalars:2.9.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.retrofit2:retrofit:2.9.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-beanutils:commons-beanutils:1.9.4=checkstyle
commons-codec:commons-codec:1.15=runtimeClasspath,testRuntimeClasspath
commons-collections:commons-collections:3.2.2=checkstyle
commons-logging:commons-logging:1.2=runtimeClasspath,testRuntimeClasspath
info.picocli:picocli:4.6.2=checkstyle
io.gsonfire:gson-fire:1.8.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.netty:netty-codec-http2:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
Expand All @@ -25,26 +37,40 @@ io.netty:netty-resolver:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.netty:netty-transport-classes-epoll:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.108.Final=runtimeClasspath,testRuntimeClasspath
io.swagger:swagger-annotations:1.5.22=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
joda-time:joda-time:2.6=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.14.15=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.14.15=testCompileClasspath,testRuntimeClasspath
net.sf.saxon:Saxon-HE:10.6=checkstyle
org.antlr:antlr4-runtime:4.9.3=checkstyle
org.apache.httpcomponents:httpclient:4.5.13=runtimeClasspath,testRuntimeClasspath
org.apache.httpcomponents:httpcore:4.4.13=runtimeClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.checkerframework:checker-qual:3.12.0=checkstyle
org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
org.javassist:javassist:3.28.0-GA=checkstyle
org.junit.jupiter:junit-jupiter-api:5.9.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.9.1=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.9.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter:5.9.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.9.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.9.1=testRuntimeClasspath
org.junit:junit-bom:5.9.1=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.10.2=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.9.1=testCompileClasspath
org.junit.jupiter:junit-jupiter-engine:5.10.2=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.10.2=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.9.1=testCompileClasspath
org.junit.jupiter:junit-jupiter:5.10.2=testRuntimeClasspath
org.junit.jupiter:junit-jupiter:5.9.1=testCompileClasspath
org.junit.platform:junit-platform-commons:1.10.2=testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.9.1=testCompileClasspath
org.junit.platform:junit-platform-engine:1.10.2=testRuntimeClasspath
org.junit:junit-bom:5.10.2=testRuntimeClasspath
org.junit:junit-bom:5.9.1=testCompileClasspath
org.mockito:mockito-core:5.12.0=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-junit-jupiter:5.12.0=testCompileClasspath,testRuntimeClasspath
org.objenesis:objenesis:3.3=testRuntimeClasspath
org.opentest4j:opentest4j:1.2.0=testCompileClasspath
org.opentest4j:opentest4j:1.3.0=testRuntimeClasspath
org.reactivestreams:reactive-streams:1.0.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.reflections:reflections:0.10.2=checkstyle
org.slf4j:slf4j-api:1.7.32=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-simple:1.7.32=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.threeten:threetenbp:1.3.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
software.amazon.awssdk:annotations:2.25.60=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
software.amazon.awssdk:apache-client:2.25.60=runtimeClasspath,testRuntimeClasspath
software.amazon.awssdk:auth:2.25.60=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/com/sailthru/sqs/MParticleClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.sailthru.sqs;

import com.mparticle.ApiClient;
import com.mparticle.client.EventsApi;
import com.mparticle.model.Batch;
import com.mparticle.model.CustomEvent;
import com.mparticle.model.CustomEventData;
import com.mparticle.model.UserIdentities;
import com.sailthru.sqs.exception.RetryLaterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import retrofit2.Call;
import retrofit2.Response;

import java.io.IOException;

public class MParticleClient {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageProcessor.class);
private static final String BASE_URL = "https://inbound.mparticle.com/s2s/v2/";

public void submit(final MParticleMessage message) throws RetryLaterException {

final Batch batch = prepareBatch(message);

final EventsApi eventsApi = getEventsApi(message.getAuthenticationKey(), message.getAuthenticationSecret());

LOGGER.debug("Attempting to send batch: {} for message: {}", batch, message);

final Call<Void> singleResult = eventsApi.uploadEvents(batch);

try {
final Response<Void> response = singleResult.execute();
LOGGER.info("Received response code: {}", response.code());

if (!response.isSuccessful()) {
throw new RetryLaterException();
}

LOGGER.info("Successfully sent message: {}", message);
} catch (IOException e) {
//Retry for all IOExceptions
throw new RetryLaterException(e);
}
}

private EventsApi getEventsApi(final String apiKey, final String apiSecret) {
final ApiClient apiClient = new ApiClient(apiKey, apiSecret);

apiClient.getAdapterBuilder().baseUrl(BASE_URL);

return apiClient.createService(EventsApi.class);
}

private Batch prepareBatch(final MParticleMessage message) {
final Batch batch = new Batch();
batch.environment(Batch.Environment.DEVELOPMENT);
batch.userIdentities(new UserIdentities()
.email(message.getProfileEmail())
);

final CustomEvent event = new CustomEvent().data(
new CustomEventData()
.eventName(message.getEventName())
.customEventType(CustomEventData.CustomEventType.valueOf(message.getEventType()))
);

event.getData().customAttributes(message.getAdditionalData());

batch.addEventsItem(event);

return batch;
}
}
72 changes: 72 additions & 0 deletions src/main/java/com/sailthru/sqs/MParticleMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.sailthru.sqs;

import java.util.Map;

public class MParticleMessage {
private String authenticationKey;
private String authenticationSecret;
private String eventName;
private String eventType;
private Map<String, String> additionalData;
private String profileEmail;

public String getAuthenticationKey() {
return authenticationKey;
}

public String getAuthenticationSecret() {
return authenticationSecret;
}

public String getEventName() {
return eventName;
}

public String getEventType() {
return eventType;
}

public Map<String, String> getAdditionalData() {
return additionalData;
}

public String getProfileEmail() {
return profileEmail;
}

public void setAuthenticationKey(String authenticationKey) {
this.authenticationKey = authenticationKey;
}

public void setAuthenticationSecret(String authenticationSecret) {
this.authenticationSecret = authenticationSecret;
}

public void setEventName(String eventName) {
this.eventName = eventName;
}

public void setEventType(String eventType) {
this.eventType = eventType;
}

public void setAdditionalData(Map<String, String> additionalData) {
this.additionalData = additionalData;
}

public void setProfileEmail(String profileEmail) {
this.profileEmail = profileEmail;
}

@Override
public String toString() {
return "MParticleMessage{" +
"authenticationKey='" + authenticationKey + '\'' +
", authenticationSecret='" + authenticationSecret + '\'' +
", eventName='" + eventName + '\'' +
", eventType='" + eventType + '\'' +
", additionalData=" + additionalData +
", profileEmail='" + profileEmail + '\'' +
'}';
}
}
64 changes: 64 additions & 0 deletions src/main/java/com/sailthru/sqs/MessageProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.sailthru.sqs;

import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.sailthru.sqs.exception.AuthenticationKeyNotProvidedException;
import com.sailthru.sqs.exception.AuthenticationSecretNotProvidedException;
import com.sailthru.sqs.exception.NoRetryException;
import com.sailthru.sqs.exception.RetryLaterException;
import com.sailthru.sqs.exception.UnparseablePayloadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.utils.StringUtils;

import java.io.IOException;

public class MessageProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageProcessor.class);

private MessageSerializer messageSerializer;
private MParticleClient mParticleClient;

public MessageProcessor() {
messageSerializer = new MessageSerializer();
mParticleClient = new MParticleClient();
}

public void process(final SQSEvent.SQSMessage sqsMessage) throws RetryLaterException, NoRetryException {
final String rawMessage = sqsMessage.getBody();
LOGGER.debug("Received message: {}", rawMessage);

final MParticleMessage message = parseAndValidateMessage(rawMessage);

getMParticleClient().submit(message);
}

private MParticleMessage parseAndValidateMessage(final String rawMessage) throws NoRetryException {
try {
final MParticleMessage message = getSerializer().deserialize(rawMessage, MParticleMessage.class);

if (StringUtils.isEmpty(message.getAuthenticationKey())) {
throw new AuthenticationKeyNotProvidedException("Authentication key not provided.");
}

if (StringUtils.isEmpty(message.getAuthenticationSecret())) {
throw new AuthenticationSecretNotProvidedException("Authentication secret not provided.");
}

return message;
} catch (IOException e) {
throw new UnparseablePayloadException(String.format("Could not deserialize message: %s", rawMessage), e);
}
}

private MParticleClient getMParticleClient() {
return mParticleClient;
}

private MessageSerializer getSerializer() {
return messageSerializer;
}

public void setMParticleClient(final MParticleClient mParticleClient) {
this.mParticleClient = mParticleClient;
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/sailthru/sqs/MessageSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sailthru.sqs;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public final class MessageSerializer {
private final ObjectMapper mapper = new ObjectMapper();

public <T> T deserialize(String content, Class<T> valueType) throws IOException {
return this.mapper.readValue(content, valueType);
}

public String serialize(Object newMessage) throws JsonProcessingException {
return this.mapper.writeValueAsString(newMessage);
}
}
Loading

0 comments on commit d091223

Please sign in to comment.