diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e0cdf1..a129f46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,53 +33,47 @@ workflows: - lumigo-orb/be-deploy: context: common + save_project_folder: false requires: - lumigo-orb/is_environment_available - - lumigo-orb/pre_build_artifacts: + - lumigo-orb/prep-it-resources: context: common requires: - lumigo-orb/is_environment_available - - lumigo-orb/integration-test-prep: - context: - - common - - java - install_maven_dependencies: true - pre_builds_available: true - run_test_cleanup: false - requires: - - lumigo-orb/be-deploy - - lumigo-orb/pre_build_artifacts - - - lumigo-orb/integration-test-cleanup: - name: pre-test-cleanup + - lumigo-orb/prep-k8s-and-operator: context: common requires: - - lumigo-orb/integration-test-prep + - lumigo-orb/is_environment_available - lumigo-orb/integration-test-parallel: context: common run_test_cleanup: false requires: - - pre-test-cleanup + - lumigo-orb/be-deploy + - lumigo-orb/prep-it-resources + - lumigo-orb/prep-k8s-and-operator - - lumigo-orb/integration-test-cleanup: - name: post-test-cleanup + - lumigo-orb/e2e-test: context: common requires: - - lumigo-orb/integration-test-parallel + - lumigo-orb/be-deploy + - lumigo-orb/prep-it-resources + - lumigo-orb/prep-k8s-and-operator - - lumigo-orb/e2e-test: + - lumigo-orb/integration-test-cleanup: + name: post-test-cleanup context: common requires: - - pre-test-cleanup + - lumigo-orb/integration-test-parallel + - lumigo-orb/e2e-test - lumigo-orb/workflow-completed-successfully: context: common requires: - - test - lumigo-orb/integration-test-parallel + - test - lumigo-orb/e2e-test - deploy: diff --git a/README.md b/README.md index c7c7a67..7712e06 100644 --- a/README.md +++ b/README.md @@ -102,3 +102,9 @@ class MyFunction implements RequestHandler { Add the environment variable `JAVA_TOOL_OPTIONS` to your Lambda functions and set it to `-Djdk.attach.allowAttachSelf=true` in addition to the manual code mentioned above. + +### Supported Instrumentation Libraries + +- Aws SDK V1 +- Aws SDK V2 +- Apache HTTP Client \ No newline at end of file diff --git a/pom.xml b/pom.xml index 806e370..4e1165a 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 2.9.8 4.5.6 4.4.10 - 5.4.2 + 5.10.2 @@ -96,7 +96,7 @@ com.amazonaws aws-java-sdk-s3 - 1.11.505 + 1.12.261 provided @@ -117,7 +117,33 @@ 3.14.2 - + + + software.amazon.awssdk + core + 2.25.45 + pom + + + software.amazon.awssdk + dynamodb + 2.25.45 + + + software.amazon.awssdk + sqs + 2.25.45 + + + software.amazon.awssdk + sns + 2.25.45 + + + software.amazon.awssdk + kinesis + 2.25.45 + diff --git a/src/main/java/io/lumigo/core/SpansContainer.java b/src/main/java/io/lumigo/core/SpansContainer.java index 04ff49d..acabae0 100644 --- a/src/main/java/io/lumigo/core/SpansContainer.java +++ b/src/main/java/io/lumigo/core/SpansContainer.java @@ -5,8 +5,9 @@ import com.amazonaws.services.lambda.runtime.Context; import io.lumigo.core.configuration.Configuration; import io.lumigo.core.network.Reporter; -import io.lumigo.core.parsers.AwsParserFactory; import io.lumigo.core.parsers.event.EventParserFactory; +import io.lumigo.core.parsers.v1.AwsSdkV1ParserFactory; +import io.lumigo.core.parsers.v2.AwsSdkV2ParserFactory; import io.lumigo.core.utils.AwsUtils; import io.lumigo.core.utils.JsonUtils; import io.lumigo.core.utils.StringUtils; @@ -21,6 +22,11 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.pmw.tinylog.Logger; +import software.amazon.awssdk.awscore.AwsResponse; +import software.amazon.awssdk.core.SdkResponse; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.core.sync.RequestBody; public class SpansContainer { @@ -354,7 +360,77 @@ public void addHttpSpan(Long startTime, Request request, Response response response.getHttpResponse().getStatusCode()) .build()) .build()); - AwsParserFactory.getParser(request.getServiceName()).parse(httpSpan, request, response); + AwsSdkV1ParserFactory.getParser(request.getServiceName()) + .safeParse(httpSpan, request, response); + httpSpans.add(httpSpan); + } + + public void addHttpSpan( + Long startTime, + final software.amazon.awssdk.core.interceptor.Context.AfterExecution context, + final ExecutionAttributes executionAttributes) { + HttpSpan httpSpan = createBaseHttpSpan(startTime); + String spanId = null; + for (Map.Entry> header : context.httpResponse().headers().entrySet()) { + if ("x-amzn-requestid".equalsIgnoreCase(header.getKey()) + || "x-amz-requestid".equalsIgnoreCase(header.getKey())) { + spanId = header.getValue().get(0); + } + } + if (spanId != null) { + httpSpan.setId(spanId); + } + httpSpan.getInfo() + .setHttpInfo( + HttpSpan.HttpInfo.builder() + .host(context.httpRequest().getUri().getHost()) + .request( + HttpSpan.HttpData.builder() + .headers( + callIfVerbose( + () -> + extractHeadersV2( + context.httpRequest() + .headers()))) + .uri( + callIfVerbose( + () -> + context.httpRequest() + .getUri() + .toString())) + .method(context.httpRequest().method().name()) + .body( + callIfVerbose( + () -> + extractBodyFromRequest( + context + .requestBody()))) + .build()) + .response( + HttpSpan.HttpData.builder() + .headers( + callIfVerbose( + () -> + extractHeadersV2( + context.httpResponse() + .headers()))) + .body( + callIfVerbose( + () -> + extractBodyFromResponse( + context + .response()))) + .statusCode(context.httpResponse().statusCode()) + .build()) + .build()); + + Logger.debug( + "Trying to extract aws custom properties for service: " + + executionAttributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME)); + AwsSdkV2ParserFactory.getParser( + executionAttributes.getAttribute(SdkExecutionAttribute.SERVICE_NAME)) + .safeParse(httpSpan, context); + httpSpans.add(httpSpan); } @@ -362,6 +438,10 @@ private static String extractHeaders(Map headers) { return JsonUtils.getObjectAsJsonString(headers); } + private static String extractHeadersV2(Map> headers) { + return JsonUtils.getObjectAsJsonString(headers); + } + private static String extractHeaders(Header[] headers) { Map headersMap = new HashMap<>(); if (headers != null) { @@ -373,27 +453,31 @@ private static String extractHeaders(Header[] headers) { } protected static String extractBodyFromRequest(Request request) { - return extractBodyFromRequest(request.getContent()); + return extractBodyFromStream(request.getContent()); + } + + protected static String extractBodyFromRequest(Optional request) { + return request.map( + requestBody -> + extractBodyFromStream( + requestBody.contentStreamProvider().newStream())) + .orElse(null); } protected static String extractBodyFromRequest(HttpUriRequest request) throws Exception { if (request instanceof HttpEntityEnclosingRequestBase) { HttpEntity entity = ((HttpEntityEnclosingRequestBase) request).getEntity(); if (entity != null) { - return extractBodyFromRequest(entity.getContent()); + return extractBodyFromStream(entity.getContent()); } } return null; } - protected static String extractBodyFromRequest(InputStream stream) { - return StringUtils.extractStringForStream(stream, MAX_STRING_SIZE); - } - protected static String extractBodyFromResponse(HttpResponse response) throws IOException { - return StringUtils.extractStringForStream( - response.getEntity() != null ? response.getEntity().getContent() : null, - MAX_STRING_SIZE); + return response.getEntity() != null + ? extractBodyFromStream(response.getEntity().getContent()) + : null; } protected static String extractBodyFromResponse(Response response) { @@ -402,6 +486,17 @@ protected static String extractBodyFromResponse(Response response) { : null; } + protected static String extractBodyFromResponse(SdkResponse response) { + if (response instanceof AwsResponse) { + return JsonUtils.getObjectAsJsonString(response.toBuilder()); + } + return null; + } + + protected static String extractBodyFromStream(InputStream stream) { + return StringUtils.extractStringForStream(stream, MAX_STRING_SIZE); + } + public String getPatchedRoot() { return String.format( "Root=%s-0000%s-%s%s", diff --git a/src/main/java/io/lumigo/core/instrumentation/agent/Loader.java b/src/main/java/io/lumigo/core/instrumentation/agent/Loader.java index 23a7164..bfb17d8 100644 --- a/src/main/java/io/lumigo/core/instrumentation/agent/Loader.java +++ b/src/main/java/io/lumigo/core/instrumentation/agent/Loader.java @@ -4,6 +4,7 @@ import static net.bytebuddy.matcher.ElementMatchers.not; import io.lumigo.core.instrumentation.impl.AmazonHttpClientInstrumentation; +import io.lumigo.core.instrumentation.impl.AmazonHttpClientV2Instrumentation; import io.lumigo.core.instrumentation.impl.ApacheHttpInstrumentation; import net.bytebuddy.agent.builder.AgentBuilder; import org.pmw.tinylog.Logger; @@ -14,17 +15,25 @@ public static void instrument(java.lang.instrument.Instrumentation inst) { ApacheHttpInstrumentation apacheHttpInstrumentation = new ApacheHttpInstrumentation(); AmazonHttpClientInstrumentation amazonHttpClientInstrumentation = new AmazonHttpClientInstrumentation(); + AmazonHttpClientV2Instrumentation amazonHttpClientV2Instrumentation = + new AmazonHttpClientV2Instrumentation(); AgentBuilder builder = new AgentBuilder.Default() .disableClassFormatChanges() .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .ignore( not(nameStartsWith("com.amazonaws.http.AmazonHttpClient")) - .and(not(nameStartsWith("org.apache.http.impl.client")))) + .and(not(nameStartsWith("org.apache.http.impl.client"))) + .and( + not( + nameStartsWith( + "software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder")))) .type(apacheHttpInstrumentation.getTypeMatcher()) .transform(apacheHttpInstrumentation.getTransformer()) .type(amazonHttpClientInstrumentation.getTypeMatcher()) - .transform(amazonHttpClientInstrumentation.getTransformer()); + .transform(amazonHttpClientInstrumentation.getTransformer()) + .type(amazonHttpClientV2Instrumentation.getTypeMatcher()) + .transform(amazonHttpClientV2Instrumentation.getTransformer()); builder.installOn(inst); Logger.debug("Finish Instrumentation"); diff --git a/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentation.java b/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentation.java index 2b75855..a624812 100644 --- a/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentation.java +++ b/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientInstrumentation.java @@ -24,7 +24,6 @@ public ElementMatcher getTypeMatcher() { @Override public AgentBuilder.Transformer.ForAdvice getTransformer() { - return new AgentBuilder.Transformer.ForAdvice() .include(Loader.class.getClassLoader()) .advice(isMethod().and(named("execute")), AmazonHttpClientAdvice.class.getName()); diff --git a/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientV2Instrumentation.java b/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientV2Instrumentation.java new file mode 100644 index 0000000..e816487 --- /dev/null +++ b/src/main/java/io/lumigo/core/instrumentation/impl/AmazonHttpClientV2Instrumentation.java @@ -0,0 +1,100 @@ +package io.lumigo.core.instrumentation.impl; + +import static net.bytebuddy.matcher.ElementMatchers.*; + +import io.lumigo.core.SpansContainer; +import io.lumigo.core.instrumentation.LumigoInstrumentationApi; +import io.lumigo.core.instrumentation.agent.Loader; +import io.lumigo.core.utils.LRUCache; +import java.util.List; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.http.SdkHttpRequest; + +public class AmazonHttpClientV2Instrumentation implements LumigoInstrumentationApi { + @Override + public ElementMatcher getTypeMatcher() { + return named("software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder"); + } + + @Override + public AgentBuilder.Transformer.ForAdvice getTransformer() { + return new AgentBuilder.Transformer.ForAdvice() + .include(Loader.class.getClassLoader()) + .advice( + isMethod().and(named("resolveExecutionInterceptors")), + AmazonHttpClientV2Advice.class.getName()); + } + + public static class AmazonHttpClientV2Advice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void methodExit( + @Advice.Return final List interceptors) { + Logger.debug("Added Lumigo TracingExecutionInterceptor"); + for (ExecutionInterceptor interceptor : interceptors) { + if (interceptor instanceof TracingExecutionInterceptor) { + return; // list already has our interceptor, return to builder + } + } + interceptors.add(new TracingExecutionInterceptor()); + } + + public static class TracingExecutionInterceptor implements ExecutionInterceptor { + public static final SpansContainer spansContainer = SpansContainer.getInstance(); + public static final LRUCache handled = new LRUCache<>(1000); + public static final LRUCache startTimeMap = new LRUCache<>(1000); + + @Override + public void beforeExecution( + final Context.BeforeExecution context, + final ExecutionAttributes executionAttributes) { + startTimeMap.put(context.request().hashCode(), System.currentTimeMillis()); + } + + @Override + public SdkHttpRequest modifyHttpRequest( + Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { + try { + SdkHttpRequest.Builder requestBuilder = context.httpRequest().toBuilder(); + requestBuilder.appendHeader("X-Amzn-Trace-Id", spansContainer.getPatchedRoot()); + return requestBuilder.build(); + } catch (Throwable e) { + Logger.debug("Unable to inject trace header", e); + } + return context.httpRequest(); + } + + @Override + public void afterExecution( + final Context.AfterExecution context, + final ExecutionAttributes executionAttributes) { + try { + if (handled.get(context.request().hashCode()) == null) { + Logger.info( + "Handling request {} from host {}", context.request().hashCode()); + spansContainer.addHttpSpan( + startTimeMap.get(context.request().hashCode()), + context, + executionAttributes); + handled.put(context.request().hashCode(), true); + } else { + Logger.warn( + "Already handle request {} for host {}", + context.request().hashCode(), + context.httpRequest().host()); + } + } catch (Throwable e) { + Logger.error(e, "Failed to send data on http response"); + } finally { + startTimeMap.remove(context.request().hashCode()); + } + } + } + } +} diff --git a/src/main/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentation.java b/src/main/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentation.java index 9b90f52..c96b7ce 100644 --- a/src/main/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentation.java +++ b/src/main/java/io/lumigo/core/instrumentation/impl/ApacheHttpInstrumentation.java @@ -24,7 +24,6 @@ public ElementMatcher getTypeMatcher() { @Override public AgentBuilder.Transformer.ForAdvice getTransformer() { - return new AgentBuilder.Transformer.ForAdvice() .include(Loader.class.getClassLoader()) .advice( diff --git a/src/main/java/io/lumigo/core/parsers/AwsParser.java b/src/main/java/io/lumigo/core/parsers/AwsParser.java deleted file mode 100644 index 3f6a47a..0000000 --- a/src/main/java/io/lumigo/core/parsers/AwsParser.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.lumigo.core.parsers; - -import com.amazonaws.Request; -import com.amazonaws.Response; -import io.lumigo.models.HttpSpan; -import java.util.List; - -public interface AwsParser { - void parse(HttpSpan span, Request request, Response response); - - default String getParameter(Request request, String key) { - - if (request.getParameters() != null - && request.getParameters().get(key) != null - && ((List) request.getParameters().get(key)).size() > 0) { - return ((List) request.getParameters().get(key)).get(0).toString(); - } - return null; - } -} diff --git a/src/main/java/io/lumigo/core/parsers/AwsParserFactory.java b/src/main/java/io/lumigo/core/parsers/AwsParserFactory.java deleted file mode 100644 index 27c6ca6..0000000 --- a/src/main/java/io/lumigo/core/parsers/AwsParserFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.lumigo.core.parsers; - -public class AwsParserFactory { - /** - * @param serviceName - AWS service name - * @return Relavant parser if exists or Default parser - */ - public static AwsParser getParser(String serviceName) { - if (serviceName == null) { - return new DefaultParser(); - } - switch (serviceName) { - case "AmazonSNS": - return new SnsParser(); - case "AmazonSQS": - return new SqsParser(); - case "AmazonKinesis": - return new KinesisParser(); - case "AmazonDynamoDB": - case "AmazonDynamoDBv2": - return new DynamoDBParser(); - default: - return new DefaultParser(); - } - } -} diff --git a/src/main/java/io/lumigo/core/parsers/DefaultParser.java b/src/main/java/io/lumigo/core/parsers/DefaultParser.java deleted file mode 100644 index 47cb774..0000000 --- a/src/main/java/io/lumigo/core/parsers/DefaultParser.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.lumigo.core.parsers; - -import com.amazonaws.Request; -import com.amazonaws.Response; -import io.lumigo.models.HttpSpan; - -public class DefaultParser implements AwsParser { - @Override - public void parse(HttpSpan span, Request request, Response response) {} -} diff --git a/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1Parser.java b/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1Parser.java new file mode 100644 index 0000000..e9608dc --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1Parser.java @@ -0,0 +1,33 @@ +package io.lumigo.core.parsers.v1; + +import com.amazonaws.Request; +import com.amazonaws.Response; +import io.lumigo.models.HttpSpan; +import java.util.List; +import org.pmw.tinylog.Logger; + +public interface AwsSdkV1Parser { + String getParserType(); + + void parse(HttpSpan span, Request request, Response response); + + default void safeParse(HttpSpan span, Request request, Response response) { + try { + Logger.debug("Start parsing aws v1 request using: " + getParserType()); + parse(span, request, response); + Logger.debug("Finish parsing aws v1 request using: " + getParserType()); + } catch (Throwable e) { + Logger.error("Failed to parse extra aws v1 data using parser: " + getParserType(), e); + } + } + + default String getParameter(Request request, String key) { + + if (request.getParameters() != null + && request.getParameters().get(key) != null + && ((List) request.getParameters().get(key)).size() > 0) { + return ((List) request.getParameters().get(key)).get(0).toString(); + } + return null; + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactory.java b/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactory.java new file mode 100644 index 0000000..f7ae2bc --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactory.java @@ -0,0 +1,26 @@ +package io.lumigo.core.parsers.v1; + +public class AwsSdkV1ParserFactory { + /** + * @param serviceName - AWS service name + * @return Relavant parser if exists or Default parser + */ + public static AwsSdkV1Parser getParser(String serviceName) { + if (serviceName == null) { + return new DoNothingV1Parser(); + } + switch (serviceName) { + case "AmazonSNS": + return new SnsV1Parser(); + case "AmazonSQS": + return new SqsV1Parser(); + case "AmazonKinesis": + return new KinesisV1Parser(); + case "AmazonDynamoDB": + case "AmazonDynamoDBv2": + return new DynamoDBV1Parser(); + default: + return new DoNothingV1Parser(); + } + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v1/DoNothingV1Parser.java b/src/main/java/io/lumigo/core/parsers/v1/DoNothingV1Parser.java new file mode 100644 index 0000000..20f0d72 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v1/DoNothingV1Parser.java @@ -0,0 +1,15 @@ +package io.lumigo.core.parsers.v1; + +import com.amazonaws.Request; +import com.amazonaws.Response; +import io.lumigo.models.HttpSpan; + +public class DoNothingV1Parser implements AwsSdkV1Parser { + @Override + public String getParserType() { + return DoNothingV1Parser.class.getName(); + } + + @Override + public void parse(HttpSpan span, Request request, Response response) {} +} diff --git a/src/main/java/io/lumigo/core/parsers/DynamoDBParser.java b/src/main/java/io/lumigo/core/parsers/v1/DynamoDBV1Parser.java similarity index 82% rename from src/main/java/io/lumigo/core/parsers/DynamoDBParser.java rename to src/main/java/io/lumigo/core/parsers/v1/DynamoDBV1Parser.java index 766c22f..e2e8ddb 100644 --- a/src/main/java/io/lumigo/core/parsers/DynamoDBParser.java +++ b/src/main/java/io/lumigo/core/parsers/v1/DynamoDBV1Parser.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import static io.lumigo.core.utils.StringUtils.dynamodbItemToHash; @@ -9,23 +9,22 @@ import io.lumigo.models.HttpSpan; import java.util.List; import java.util.Map; -import org.pmw.tinylog.Logger; -public class DynamoDBParser implements AwsParser { +public class DynamoDBV1Parser implements AwsSdkV1Parser { + @Override + public String getParserType() { + return DynamoDBV1Parser.class.getName(); + } + @Override public void parse(HttpSpan span, Request request, Response response) { - try { - String messageId = extractMessageId(request.getOriginalRequest()); - if (messageId != null) span.getInfo().setMessageId(messageId); - String tableName = extractTableName(request.getOriginalRequest()); - if (tableName != null) span.getInfo().setResourceName(tableName); - } catch (Exception e) { - Logger.error(e, "Failed to parse for DynamoDB request"); - } + String messageId = extractMessageId(request.getOriginalRequest()); + if (messageId != null) span.getInfo().setMessageId(messageId); + String tableName = extractTableName(request.getOriginalRequest()); + if (tableName != null) span.getInfo().setResourceName(tableName); } private String extractMessageId(AmazonWebServiceRequest request) { - if (request instanceof PutItemRequest) { return dynamodbItemToHash(((PutItemRequest) request).getItem()); } else if (request instanceof UpdateItemRequest) { @@ -46,7 +45,6 @@ private String extractMessageId(AmazonWebServiceRequest request) { } private String extractTableName(AmazonWebServiceRequest request) { - if (request instanceof PutItemRequest) { return ((PutItemRequest) request).getTableName(); } else if (request instanceof UpdateItemRequest) { diff --git a/src/main/java/io/lumigo/core/parsers/KinesisParser.java b/src/main/java/io/lumigo/core/parsers/v1/KinesisV1Parser.java similarity index 58% rename from src/main/java/io/lumigo/core/parsers/KinesisParser.java rename to src/main/java/io/lumigo/core/parsers/v1/KinesisV1Parser.java index 271b5a0..da78f67 100644 --- a/src/main/java/io/lumigo/core/parsers/KinesisParser.java +++ b/src/main/java/io/lumigo/core/parsers/v1/KinesisV1Parser.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import com.amazonaws.Request; import com.amazonaws.Response; @@ -11,29 +11,29 @@ import java.util.List; import org.pmw.tinylog.Logger; -public class KinesisParser implements AwsParser { +public class KinesisV1Parser implements AwsSdkV1Parser { + @Override + public String getParserType() { + return KinesisV1Parser.class.getName(); + } + @Override public void parse(HttpSpan span, Request request, Response response) { - try { - if (request.getOriginalRequest() instanceof PutRecordRequest) { - span.getInfo() - .setResourceName( - ((PutRecordRequest) request.getOriginalRequest()).getStreamName()); - } - if (request.getOriginalRequest() instanceof PutRecordsRequest) { - span.getInfo() - .setResourceName( - ((PutRecordsRequest) request.getOriginalRequest()).getStreamName()); - } - List messageIds = extractMessageIds(response.getAwsResponse()); - if (messageIds.size() > 0) span.getInfo().setMessageIds(messageIds); - } catch (Exception e) { - Logger.error(e, "Failed to extract parse for Kinesis request"); + if (request.getOriginalRequest() instanceof PutRecordRequest) { + span.getInfo() + .setResourceName( + ((PutRecordRequest) request.getOriginalRequest()).getStreamName()); } + if (request.getOriginalRequest() instanceof PutRecordsRequest) { + span.getInfo() + .setResourceName( + ((PutRecordsRequest) request.getOriginalRequest()).getStreamName()); + } + List messageIds = extractMessageIds(response.getAwsResponse()); + if (!messageIds.isEmpty()) span.getInfo().setMessageIds(messageIds); } private List extractMessageIds(Object response) { - List result = new LinkedList<>(); if (response instanceof PutRecordsResult) { ((PutRecordsResult) response) diff --git a/src/main/java/io/lumigo/core/parsers/SnsParser.java b/src/main/java/io/lumigo/core/parsers/v1/SnsV1Parser.java similarity index 84% rename from src/main/java/io/lumigo/core/parsers/SnsParser.java rename to src/main/java/io/lumigo/core/parsers/v1/SnsV1Parser.java index 9552985..9a7a64d 100644 --- a/src/main/java/io/lumigo/core/parsers/SnsParser.java +++ b/src/main/java/io/lumigo/core/parsers/v1/SnsV1Parser.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import com.amazonaws.Request; import com.amazonaws.Response; @@ -6,7 +6,12 @@ import io.lumigo.models.HttpSpan; import org.pmw.tinylog.Logger; -public class SnsParser implements AwsParser { +public class SnsV1Parser implements AwsSdkV1Parser { + @Override + public String getParserType() { + return SnsV1Parser.class.getName(); + } + @Override public void parse(HttpSpan span, Request request, Response response) { String topicArn = getParameter(request, "TopicArn"); diff --git a/src/main/java/io/lumigo/core/parsers/SqsParser.java b/src/main/java/io/lumigo/core/parsers/v1/SqsV1Parser.java similarity index 84% rename from src/main/java/io/lumigo/core/parsers/SqsParser.java rename to src/main/java/io/lumigo/core/parsers/v1/SqsV1Parser.java index a4b21dc..d48e3ba 100644 --- a/src/main/java/io/lumigo/core/parsers/SqsParser.java +++ b/src/main/java/io/lumigo/core/parsers/v1/SqsV1Parser.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import com.amazonaws.Request; import com.amazonaws.Response; @@ -7,7 +7,12 @@ import io.lumigo.models.HttpSpan; import org.pmw.tinylog.Logger; -public class SqsParser implements AwsParser { +public class SqsV1Parser implements AwsSdkV1Parser { + @Override + public String getParserType() { + return SqsV1Parser.class.getName(); + } + @Override public void parse(HttpSpan span, Request request, Response response) { if (request.getOriginalRequest() instanceof SendMessageRequest) { @@ -24,7 +29,7 @@ private String extractMessageId(Object response) { try { if (response instanceof SendMessageResult) { String messageId = ((SendMessageResult) response).getMessageId(); - Logger.debug("Got getMessageId : " + messageId); + Logger.debug("Got messageId : " + messageId); return messageId; } else { Logger.error("Failed to extract messageId for SQS response"); diff --git a/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2Parser.java new file mode 100644 index 0000000..c878aa7 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2Parser.java @@ -0,0 +1,22 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.models.HttpSpan; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.interceptor.Context; + +public interface AwsSdkV2Parser { + String getParserType(); + + void parse(HttpSpan span, Context.AfterExecution context); + + default void safeParse(HttpSpan span, Context.AfterExecution context) { + try { + Logger.debug("Start parsing aws v2 request using: " + getParserType()); + parse(span, context); + Logger.debug("Finish parsing aws v2 request using: " + getParserType()); + } catch (Throwable e) { + Logger.error( + "Failed to parse extra aws sdk v2 data using parser: " + getParserType(), e); + } + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactory.java b/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactory.java new file mode 100644 index 0000000..93b4ba7 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactory.java @@ -0,0 +1,25 @@ +package io.lumigo.core.parsers.v2; + +public class AwsSdkV2ParserFactory { + /** + * @param serviceName - AWS service name + * @return Relavant parser if exists or Default parser + */ + public static AwsSdkV2Parser getParser(String serviceName) { + if (serviceName == null) { + return new DoNothingV2Parser(); + } + switch (serviceName) { + case "Sns": + return new SnsV2Parser(); + case "Sqs": + return new SqsV2Parser(); + case "Kinesis": + return new KinesisV2Parser(); + case "DynamoDb": + return new DynamoDBV2Parser(); + default: + return new DoNothingV2Parser(); + } + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/DoNothingV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/DoNothingV2Parser.java new file mode 100644 index 0000000..7389bf7 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/DoNothingV2Parser.java @@ -0,0 +1,14 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.models.HttpSpan; +import software.amazon.awssdk.core.interceptor.Context; + +public class DoNothingV2Parser implements AwsSdkV2Parser { + @Override + public String getParserType() { + return DoNothingV2Parser.class.getName(); + } + + @Override + public void parse(HttpSpan span, Context.AfterExecution context) {} +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/DynamoDBV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/DynamoDBV2Parser.java new file mode 100644 index 0000000..6c870be --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/DynamoDBV2Parser.java @@ -0,0 +1,72 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.core.utils.AwsSdkV2Utils; +import io.lumigo.models.HttpSpan; +import java.util.List; +import java.util.Map; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.services.dynamodb.model.*; + +public class DynamoDBV2Parser implements AwsSdkV2Parser { + @Override + public String getParserType() { + return DynamoDBV2Parser.class.getName(); + } + + public void parse(HttpSpan span, Context.AfterExecution context) { + SdkRequest request = context.request(); + if (context.request().getValueForField("TableName", String.class).isPresent()) { + context.request() + .getValueForField("TableName", String.class) + .ifPresent( + tableName -> { + span.getInfo().setResourceName(tableName); + Logger.debug("Parsed TableName : " + tableName); + }); + } else if (request instanceof BatchWriteItemRequest + && ((BatchWriteItemRequest) request).hasRequestItems()) { + ((BatchWriteItemRequest) request) + .requestItems().keySet().stream() + .findFirst() + .ifPresent( + tableName -> { + span.getInfo().setResourceName(tableName); + Logger.debug("Parsed TableName : " + tableName); + }); + } else if (request instanceof BatchGetItemRequest) { + ((BatchGetItemRequest) request) + .requestItems().keySet().stream() + .findFirst() + .ifPresent( + tableName -> { + span.getInfo().setResourceName(tableName); + Logger.debug("Parsed TableName : " + tableName); + }); + } else { + Logger.warn("Failed to extract TableName form DynamoDB request"); + } + span.getInfo().setMessageId(extractMessageId(context.request())); + } + + private String extractMessageId(SdkRequest request) { + if (request instanceof PutItemRequest) { + return AwsSdkV2Utils.calculateItemHash(((PutItemRequest) request).item()); + } else if (request instanceof UpdateItemRequest) { + return AwsSdkV2Utils.calculateItemHash(((UpdateItemRequest) request).key()); + } else if (request instanceof DeleteItemRequest) { + return AwsSdkV2Utils.calculateItemHash(((DeleteItemRequest) request).key()); + } else if (request instanceof BatchWriteItemRequest) { + Map> requests = + ((BatchWriteItemRequest) request).requestItems(); + WriteRequest firstRequest = requests.entrySet().iterator().next().getValue().get(0); + if (firstRequest.putRequest() != null) { + return AwsSdkV2Utils.calculateItemHash(firstRequest.putRequest().item()); + } else if (firstRequest.deleteRequest() != null) { + return AwsSdkV2Utils.calculateItemHash(firstRequest.deleteRequest().key()); + } + } + return null; + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/KinesisV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/KinesisV2Parser.java new file mode 100644 index 0000000..6cb5e91 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/KinesisV2Parser.java @@ -0,0 +1,49 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.models.HttpSpan; +import java.util.LinkedList; +import java.util.List; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.services.kinesis.model.PutRecordResponse; +import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse; + +public class KinesisV2Parser implements AwsSdkV2Parser { + @Override + public String getParserType() { + return KinesisV2Parser.class.getName(); + } + + @Override + public void parse(HttpSpan span, Context.AfterExecution context) { + if (context.request().getValueForField("StreamName", String.class).isPresent()) { + context.request() + .getValueForField("StreamName", String.class) + .ifPresent( + streamName -> { + span.getInfo().setResourceName(streamName); + Logger.debug("Parsed StreamName : " + streamName); + }); + } + List messageIds = extractMessageIds(context.response()); + if (!messageIds.isEmpty()) span.getInfo().setMessageIds(messageIds); + } + + private List extractMessageIds(Object response) { + List messageIds = new LinkedList<>(); + if (response instanceof PutRecordsResponse) { + ((PutRecordsResponse) response) + .records() + .forEach( + putRecordsResultEntry -> + messageIds.add(putRecordsResultEntry.sequenceNumber())); + return messageIds; + } + if (response instanceof PutRecordResponse) { + messageIds.add(((PutRecordResponse) response).sequenceNumber()); + return messageIds; + } + Logger.error("Failed to extract messageIds for Kinesis response"); + return messageIds; + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/SnsV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/SnsV2Parser.java new file mode 100644 index 0000000..3218174 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/SnsV2Parser.java @@ -0,0 +1,45 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.models.HttpSpan; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.SdkResponse; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.services.sns.model.PublishResponse; + +public class SnsV2Parser implements AwsSdkV2Parser { + @Override + public String getParserType() { + return SnsV2Parser.class.getName(); + } + + @Override + public void parse(HttpSpan span, Context.AfterExecution context) { + if (context.request().getValueForField("TopicArn", String.class).isPresent()) { + context.request() + .getValueForField("TopicArn", String.class) + .ifPresent( + topicArn -> { + Logger.debug("Parsed topicArn : " + topicArn); + span.getInfo().setResourceName(topicArn); + span.getInfo().setTargetArn(topicArn); + }); + } else { + Logger.warn("Failed to extract topicArn"); + } + span.getInfo().setMessageId(extractMessageId(context.response())); + } + + private String extractMessageId(SdkResponse response) { + try { + if (response instanceof PublishResponse) { + return ((PublishResponse) response).messageId(); + } else { + Logger.error("Failed to extract messageId for SNS response"); + return null; + } + } catch (Exception e) { + Logger.error(e, "Failed to extract messageId for SNS response"); + return null; + } + } +} diff --git a/src/main/java/io/lumigo/core/parsers/v2/SqsV2Parser.java b/src/main/java/io/lumigo/core/parsers/v2/SqsV2Parser.java new file mode 100644 index 0000000..b510712 --- /dev/null +++ b/src/main/java/io/lumigo/core/parsers/v2/SqsV2Parser.java @@ -0,0 +1,44 @@ +package io.lumigo.core.parsers.v2; + +import io.lumigo.models.HttpSpan; +import org.pmw.tinylog.Logger; +import software.amazon.awssdk.core.SdkResponse; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.services.sqs.model.SendMessageResponse; + +public class SqsV2Parser implements AwsSdkV2Parser { + @Override + public String getParserType() { + return SqsV2Parser.class.getName(); + } + + @Override + public void parse(HttpSpan span, Context.AfterExecution context) { + if (context.request().getValueForField("QueueUrl", String.class).isPresent()) { + context.request() + .getValueForField("QueueUrl", String.class) + .ifPresent( + queueUrl -> { + span.getInfo().setResourceName(queueUrl); + Logger.debug("Parsed queueUrl : " + queueUrl); + }); + } else { + Logger.warn("Failed to extract queueUrl form SQS request"); + } + span.getInfo().setMessageId(extractMessageId(context.response())); + } + + private String extractMessageId(SdkResponse response) { + try { + if (response instanceof SendMessageResponse) { + return ((SendMessageResponse) response).messageId(); + } else { + Logger.error("Failed to extract messageId for SQS response"); + return null; + } + } catch (Exception e) { + Logger.error(e, "Failed to extract messageId for SQS response"); + return null; + } + } +} diff --git a/src/main/java/io/lumigo/core/utils/AwsSdkV2Utils.java b/src/main/java/io/lumigo/core/utils/AwsSdkV2Utils.java new file mode 100644 index 0000000..7432232 --- /dev/null +++ b/src/main/java/io/lumigo/core/utils/AwsSdkV2Utils.java @@ -0,0 +1,47 @@ +package io.lumigo.core.utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.experimental.UtilityClass; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +@UtilityClass +public class AwsSdkV2Utils { + + public String calculateItemHash( + Map item) { + Map simpleMap = AwsSdkV2Utils.convertAttributeMapToSimpleMap(item); + return StringUtils.buildMd5Hash(JsonUtils.getObjectAsJsonString(simpleMap)); + } + + public static Map convertAttributeMapToSimpleMap( + Map attributeValueMap) { + Map simpleMap = new HashMap<>(); + attributeValueMap.forEach( + (key, value) -> simpleMap.put(key, attributeValueToObject(value))); + return simpleMap; + } + + private static Object attributeValueToObject(AttributeValue value) { + if (value == null) { + return null; + } else if (value.s() != null) { + return value.s(); + } else if (value.n() != null) { + return value.n(); + } else if (value.bool() != null) { + return value.bool(); + } else if (value.l() != null && !value.l().isEmpty()) { + List list = new ArrayList<>(); + for (AttributeValue v : value.l()) { + list.add(attributeValueToObject(v)); + } + return list; + } else if (value.m() != null && !value.m().isEmpty()) { + return convertAttributeMapToSimpleMap(value.m()); + } + return null; + } +} diff --git a/src/main/java/io/lumigo/core/utils/AwsUtils.java b/src/main/java/io/lumigo/core/utils/AwsUtils.java index b78867c..440a619 100644 --- a/src/main/java/io/lumigo/core/utils/AwsUtils.java +++ b/src/main/java/io/lumigo/core/utils/AwsUtils.java @@ -166,7 +166,7 @@ public static TriggeredBy extractTriggeredByFromEvent(Object event) { Logger.info("Found triggered by handler to event {}", event.getClass().getName()); return triggeredBy; - } catch (RuntimeException e) { + } catch (Throwable e) { Logger.error(e, "Failed to extract triggerBy data"); triggeredBy.setTriggeredBy(TRIGGERED_BY_FALLBACK); return triggeredBy; diff --git a/src/test/java/io/lumigo/core/SpansContainerTest.java b/src/test/java/io/lumigo/core/SpansContainerTest.java index 98b2650..a07fc30 100644 --- a/src/test/java/io/lumigo/core/SpansContainerTest.java +++ b/src/test/java/io/lumigo/core/SpansContainerTest.java @@ -22,9 +22,7 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -41,6 +39,14 @@ import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.services.sns.model.PublishRequest; +import software.amazon.awssdk.services.sns.model.PublishResponse; class SpansContainerTest { private static final char ch = '*'; @@ -378,7 +384,9 @@ void add_aws_http_span_with_spnid_from_header_amzn() throws Exception { "{\n" + " \"started\":1559127760071,\n" + " \"ended\":1559127760085,\n" - + " \"id\":\"cc9ceb9c-dad2-4762-8f0c-147408bdc063\",\n" + + " \"id\":" + + actualSpan.getId() + + ",\n" + " \"type\":\"http\",\n" + " \"transactionId\":\"3\",\n" + " \"account\":\"1111\",\n" @@ -486,6 +494,93 @@ void add_aws_http_span_with_spnid_from_header_amz() throws Exception { new Customization("ended", (o1, o2) -> o1 != null))); } + @DisplayName("AWS SDK V2 request") + @Test + void add_aws_sdk_v2_http_span() throws Exception { + Map> headers = new HashMap<>(); + headers.put("x-amz-requestid", Collections.singletonList("id123")); + + PublishRequest request = PublishRequest.builder().topicArn("topic").build(); + PublishResponse response = + PublishResponse.builder().messageId("fee47356-6f6a-58c8-96dc-26d8aaa4631a").build(); + + SdkHttpRequest sdkHttpRequest = + SdkHttpRequest.builder() + .uri(new URI("https://sns.amazonaws.com")) + .headers(headers) + .method(SdkHttpMethod.GET) + .build(); + + SdkHttpResponse sdkHttpResponse = + SdkHttpResponse.builder().headers(headers).statusCode(200).build(); + + software.amazon.awssdk.core.interceptor.Context.AfterExecution requestContext = + InterceptorContext.builder() + .request(request) + .httpRequest(sdkHttpRequest) + .response(response) + .httpResponse(sdkHttpResponse) + .build(); + ExecutionAttributes executionAttributes = + ExecutionAttributes.builder() + .put(SdkExecutionAttribute.SERVICE_NAME, "Sns") + .build(); + + spansContainer.init(createMockedEnv(), reporter, context, null); + long startTime = System.currentTimeMillis(); + + spansContainer.addHttpSpan(startTime, requestContext, executionAttributes); + + HttpSpan actualSpan = spansContainer.getHttpSpans().get(0); + String expectedSpan = + "{\n" + + " \"started\":1559127760071,\n" + + " \"ended\":1559127760085,\n" + + " \"id\":\"cc9ceb9c-dad2-4762-8f0c-147408bdc063\",\n" + + " \"type\":\"http\",\n" + + " \"transactionId\":\"3\",\n" + + " \"account\":\"1111\",\n" + + " \"region\":\"us-west-2\",\n" + + " \"token\":null,\n" + + " \"info\":{\n" + + " \"tracer\":{\n" + + " \"version\":\"1.0\"\n" + + " },\n" + + " \"traceId\":{\n" + + " \"Root\":\"1-2-3\"\n" + + " },\n" + + " \"httpInfo\":{\n" + + " \"host\":\"sns.amazonaws.com\",\n" + + " \"request\":{\n" + + " \"headers\":\"{\\\"x-amz-requestid\\\":[\\\"id123\\\"]}\",\n" + + " \"body\":null,\n" + + " \"uri\":\"https://sns.amazonaws.com\",\n" + + " \"statusCode\":null,\n" + + " \"method\":GET\n" + + " },\n" + + " \"response\":{\n" + + " \"headers\":\"{\\\"x-amz-requestid\\\":[\\\"id123\\\"]}\",\n" + + " \"body\":\"{\\\"messageId\\\":\\\"fee47356-6f6a-58c8-96dc-26d8aaa4631a\\\",\\\"sequenceNumber\\\":null}\", \n" + + " \"uri\":null,\n" + + " \"statusCode\":200,\n" + + " \"method\":null\n" + + " }\n" + + " }\n" + + " },\n" + + " \"parentId\":\"3n2783hf7823hdui32\"\n" + + "}"; + + JSONAssert.assertEquals( + expectedSpan, + JsonUtils.getObjectAsJsonString(actualSpan), + new CustomComparator( + JSONCompareMode.LENIENT, + new Customization("info.tracer.version", (o1, o2) -> o1 != null), + new Customization("id", (o1, o2) -> o1.equals("id123")), + new Customization("started", (o1, o2) -> o1 != null), + new Customization("ended", (o1, o2) -> o1 != null))); + } + @DisplayName("Extract body from request") @Test void test_extract_body_from_request() throws Exception { diff --git a/src/test/java/io/lumigo/core/parsers/AwsParserFactoryTest.java b/src/test/java/io/lumigo/core/parsers/AwsParserFactoryTest.java deleted file mode 100644 index 3d32cbe..0000000 --- a/src/test/java/io/lumigo/core/parsers/AwsParserFactoryTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.lumigo.core.parsers; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -class AwsParserFactoryTest { - - @Test - public void test_check_non_supported_value() { - assertEquals(DefaultParser.class, AwsParserFactory.getParser("Not supported").getClass()); - } - - @Test - public void test_check_null_value() { - assertEquals(DefaultParser.class, AwsParserFactory.getParser(null).getClass()); - } - - @Test - public void test_check_sns_value() { - assertEquals(SnsParser.class, AwsParserFactory.getParser("AmazonSNS").getClass()); - } - - @Test - public void test_check_sqs_value() { - assertEquals(SqsParser.class, AwsParserFactory.getParser("AmazonSQS").getClass()); - } - - @Test - public void test_check_kinesis_value() { - assertEquals(KinesisParser.class, AwsParserFactory.getParser("AmazonKinesis").getClass()); - } - - @Test - public void test_check_dynamodb_value() { - assertEquals(DynamoDBParser.class, AwsParserFactory.getParser("AmazonDynamoDB").getClass()); - } -} diff --git a/src/test/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactoryTest.java b/src/test/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactoryTest.java new file mode 100644 index 0000000..f6758f6 --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v1/AwsSdkV1ParserFactoryTest.java @@ -0,0 +1,43 @@ +package io.lumigo.core.parsers.v1; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class AwsSdkV1ParserFactoryTest { + + @Test + public void test_check_non_supported_value() { + assertEquals( + DoNothingV1Parser.class, + AwsSdkV1ParserFactory.getParser("Not supported").getClass()); + } + + @Test + public void test_check_null_value() { + assertEquals(DoNothingV1Parser.class, AwsSdkV1ParserFactory.getParser(null).getClass()); + } + + @Test + public void test_check_sns_value() { + assertEquals(SnsV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonSNS").getClass()); + } + + @Test + public void test_check_sqs_value() { + assertEquals(SqsV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonSQS").getClass()); + } + + @Test + public void test_check_kinesis_value() { + assertEquals( + KinesisV1Parser.class, AwsSdkV1ParserFactory.getParser("AmazonKinesis").getClass()); + } + + @Test + public void test_check_dynamodb_value() { + assertEquals( + DynamoDBV1Parser.class, + AwsSdkV1ParserFactory.getParser("AmazonDynamoDB").getClass()); + } +} diff --git a/src/test/java/io/lumigo/core/parsers/DynamoDBParserTest.java b/src/test/java/io/lumigo/core/parsers/v1/DynamoDBV1ParserTest.java similarity index 90% rename from src/test/java/io/lumigo/core/parsers/DynamoDBParserTest.java rename to src/test/java/io/lumigo/core/parsers/v1/DynamoDBV1ParserTest.java index fbbe2fe..db074f3 100644 --- a/src/test/java/io/lumigo/core/parsers/DynamoDBParserTest.java +++ b/src/test/java/io/lumigo/core/parsers/v1/DynamoDBV1ParserTest.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @@ -16,10 +16,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -class DynamoDBParserTest { +class DynamoDBV1ParserTest { private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); - private DynamoDBParser dynamoDBParser = new DynamoDBParser(); + private DynamoDBV1Parser dynamoDBParser = new DynamoDBV1Parser(); @Mock Request request; @Mock GetItemRequest getItemRequest; @Mock BatchGetItemRequest batchGetItemRequest; @@ -42,7 +42,7 @@ void setUp() { void test_parse_ddb_unknown_request() { when(request.getOriginalRequest()).thenReturn(AmazonWebServiceRequest.NOOP); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); assertEquals(span, expectedSpan); @@ -53,7 +53,7 @@ void test_parse_ddb_get_item() { when(getItemRequest.getTableName()).thenReturn("tableName"); when(request.getOriginalRequest()).thenReturn(getItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -68,7 +68,7 @@ void test_parse_ddb_batch_get_item() { .thenReturn(Collections.singletonMap("tableName", new KeysAndAttributes())); when(request.getOriginalRequest()).thenReturn(batchGetItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -83,7 +83,7 @@ void test_parse_ddb_put_item() { when(putItemRequest.getItem()).thenReturn(item); when(request.getOriginalRequest()).thenReturn(putItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -102,7 +102,7 @@ void test_parse_ddb_update_item() { when(updateItemRequest.getKey()).thenReturn(item); when(request.getOriginalRequest()).thenReturn(updateItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -121,7 +121,7 @@ void test_parse_ddb_delete_item() { when(deleteItemRequest.getKey()).thenReturn(item); when(request.getOriginalRequest()).thenReturn(deleteItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -142,7 +142,7 @@ void test_parse_ddb_batch_write_item() { "tableName", List.of(new WriteRequest(new PutRequest(item))))); when(request.getOriginalRequest()).thenReturn(batchWriteItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() @@ -163,7 +163,7 @@ void test_parse_ddb_batch_delete_item() { "tableName", List.of(new WriteRequest(new DeleteRequest(item))))); when(request.getOriginalRequest()).thenReturn(batchWriteItemRequest); - dynamoDBParser.parse(span, request, response); + dynamoDBParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder() diff --git a/src/test/java/io/lumigo/core/parsers/KinesisParserTest.java b/src/test/java/io/lumigo/core/parsers/v1/KinesisV1ParserTest.java similarity index 89% rename from src/test/java/io/lumigo/core/parsers/KinesisParserTest.java rename to src/test/java/io/lumigo/core/parsers/v1/KinesisV1ParserTest.java index d36fbee..b66e5c6 100644 --- a/src/test/java/io/lumigo/core/parsers/KinesisParserTest.java +++ b/src/test/java/io/lumigo/core/parsers/v1/KinesisV1ParserTest.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -16,10 +16,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -class KinesisParserTest { +class KinesisV1ParserTest { private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); - KinesisParser kinesisParser = new KinesisParser(); + KinesisV1Parser kinesisParser = new KinesisV1Parser(); @Mock Request request; @Mock HttpResponse httpResponse; @Mock PutRecordsRequest putRecordsRequest; @@ -37,7 +37,7 @@ void setUp() { void test_parse_kinesis_parser_with_no_data() { when(request.getParameters()).thenReturn(new HashMap<>()); - kinesisParser.parse(span, request, new Response(null, httpResponse)); + kinesisParser.safeParse(span, request, new Response(null, httpResponse)); assertNull(span.getInfo().getResourceName()); assertNull(span.getInfo().getTargetArn()); @@ -53,7 +53,7 @@ void test_parse_kinesis_put_record_simple_flow() { when(putRecordRequest.getStreamName()).thenReturn("streamName"); when(request.getOriginalRequest()).thenReturn(putRecordRequest); - kinesisParser.parse(span, request, response); + kinesisParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); expectedSpan.getInfo().setResourceName("streamName"); @@ -67,7 +67,7 @@ void test_parse_kinesis_put_record_with_exception() { when(putRecordResult.getSequenceNumber()).thenThrow(new RuntimeException()); when(request.getParameters()).thenReturn(new HashMap<>()); - kinesisParser.parse(span, request, response); + kinesisParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); assertEquals(span, expectedSpan); @@ -85,7 +85,7 @@ void test_parse_kinesis_put_records_simple_flow() { when(putRecordsRequest.getStreamName()).thenReturn("streamName"); when(request.getOriginalRequest()).thenReturn(putRecordsRequest); - kinesisParser.parse(span, request, response); + kinesisParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); expectedSpan.getInfo().setResourceName("streamName"); @@ -98,7 +98,7 @@ void test_parse_kinesis_put_records_with_exception_from_request() { response = new Response(putRecordsResult, httpResponse); when(request.getOriginalRequest()).thenThrow(new RuntimeException()); - kinesisParser.parse(span, request, response); + kinesisParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); assertEquals(span, expectedSpan); @@ -110,7 +110,7 @@ void test_parse_kinesis_put_records_with_exception_from_response() { when(putRecordsResult.getRecords()).thenThrow(new RuntimeException()); when(request.getParameters()).thenReturn(new HashMap<>()); - kinesisParser.parse(span, request, response); + kinesisParser.safeParse(span, request, response); HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); assertEquals(span, expectedSpan); diff --git a/src/test/java/io/lumigo/core/parsers/SnsParserTest.java b/src/test/java/io/lumigo/core/parsers/v1/SnsV1ParserTest.java similarity index 83% rename from src/test/java/io/lumigo/core/parsers/SnsParserTest.java rename to src/test/java/io/lumigo/core/parsers/v1/SnsV1ParserTest.java index 0e971b0..0b90e0b 100644 --- a/src/test/java/io/lumigo/core/parsers/SnsParserTest.java +++ b/src/test/java/io/lumigo/core/parsers/v1/SnsV1ParserTest.java @@ -1,6 +1,7 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; import com.amazonaws.Request; @@ -17,10 +18,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -class SnsParserTest { +class SnsV1ParserTest { private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); - SnsParser SnsParser = new SnsParser(); + SnsV1Parser SnsParser = new SnsV1Parser(); @Mock Request request; @Mock HttpResponse httpResponse; @Mock PublishResult snsResult; @@ -39,7 +40,7 @@ void test_parse_sns_with_full_details() { parameters.put("TopicArn", Arrays.asList("topic")); when(request.getParameters()).thenReturn(parameters); - SnsParser.parse(span, request, response); + SnsParser.safeParse(span, request, response); assertEquals("topic", span.getInfo().getResourceName()); assertEquals("topic", span.getInfo().getTargetArn()); @@ -50,7 +51,7 @@ void test_parse_sns_with_full_details() { void test_parse_sns_with_no_data() { when(request.getParameters()).thenReturn(new HashMap<>()); - SnsParser.parse(span, request, new Response(null, httpResponse)); + SnsParser.safeParse(span, request, new Response(null, httpResponse)); assertNull(span.getInfo().getResourceName()); assertNull(span.getInfo().getTargetArn()); @@ -62,7 +63,7 @@ void test_parse_sns_with_exception() { when(snsResult.getMessageId()).thenThrow(new RuntimeException()); when(request.getParameters()).thenReturn(new HashMap<>()); - SnsParser.parse(span, request, response); + SnsParser.safeParse(span, request, response); assertNull(span.getInfo().getResourceName()); assertNull(span.getInfo().getTargetArn()); diff --git a/src/test/java/io/lumigo/core/parsers/SqsParserTest.java b/src/test/java/io/lumigo/core/parsers/v1/SqsV1ParserTest.java similarity index 87% rename from src/test/java/io/lumigo/core/parsers/SqsParserTest.java rename to src/test/java/io/lumigo/core/parsers/v1/SqsV1ParserTest.java index 854624d..5ae06fc 100644 --- a/src/test/java/io/lumigo/core/parsers/SqsParserTest.java +++ b/src/test/java/io/lumigo/core/parsers/v1/SqsV1ParserTest.java @@ -1,4 +1,4 @@ -package io.lumigo.core.parsers; +package io.lumigo.core.parsers.v1; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -16,10 +16,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -class SqsParserTest { +class SqsV1ParserTest { private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); - SqsParser sqsParser = new SqsParser(); + SqsV1Parser sqsParser = new SqsV1Parser(); @Mock Request request; @Mock HttpResponse httpResponse; @Mock SendMessageResult sqsResult; @@ -38,7 +38,7 @@ void test_parse_sqs_with_full_details() { when(sqsRequest.getQueueUrl()).thenReturn("queueUrl"); when(request.getOriginalRequest()).thenReturn(sqsRequest); - sqsParser.parse(span, request, response); + sqsParser.safeParse(span, request, response); assertEquals("queueUrl", span.getInfo().getResourceName()); assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); @@ -48,7 +48,7 @@ void test_parse_sqs_with_full_details() { void test_parse_sqs_with_no_data() { when(request.getParameters()).thenReturn(new HashMap<>()); - sqsParser.parse(span, request, new Response(null, httpResponse)); + sqsParser.safeParse(span, request, new Response(null, httpResponse)); assertNull(span.getInfo().getResourceName()); assertNull(span.getInfo().getMessageId()); @@ -59,7 +59,7 @@ void test_parse_sqs_with_exception() { when(sqsResult.getMessageId()).thenThrow(new RuntimeException()); when(request.getParameters()).thenReturn(new HashMap<>()); - sqsParser.parse(span, request, response); + sqsParser.safeParse(span, request, response); assertNull(span.getInfo().getResourceName()); assertNull(span.getInfo().getMessageId()); diff --git a/src/test/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactoryTest.java b/src/test/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactoryTest.java new file mode 100644 index 0000000..c78f64d --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v2/AwsSdkV2ParserFactoryTest.java @@ -0,0 +1,41 @@ +package io.lumigo.core.parsers.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class AwsSdkV2ParserFactoryTest { + + @Test + public void test_check_non_supported_value() { + assertEquals( + DoNothingV2Parser.class, + AwsSdkV2ParserFactory.getParser("Not supported").getClass()); + } + + @Test + public void test_check_null_value() { + assertEquals(DoNothingV2Parser.class, AwsSdkV2ParserFactory.getParser(null).getClass()); + } + + @Test + public void test_check_sns_value_v2() { + assertEquals(SnsV2Parser.class, AwsSdkV2ParserFactory.getParser("Sns").getClass()); + } + + @Test + public void test_check_sqs_value_v2() { + assertEquals(SqsV2Parser.class, AwsSdkV2ParserFactory.getParser("Sqs").getClass()); + } + + @Test + public void test_check_kinesis_value_v2() { + assertEquals(KinesisV2Parser.class, AwsSdkV2ParserFactory.getParser("Kinesis").getClass()); + } + + @Test + public void test_check_dynamodb_value_v2() { + assertEquals( + DynamoDBV2Parser.class, AwsSdkV2ParserFactory.getParser("DynamoDb").getClass()); + } +} diff --git a/src/test/java/io/lumigo/core/parsers/v2/DynamoDBV2ParserTest.java b/src/test/java/io/lumigo/core/parsers/v2/DynamoDBV2ParserTest.java new file mode 100644 index 0000000..8706ba2 --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v2/DynamoDBV2ParserTest.java @@ -0,0 +1,197 @@ +package io.lumigo.core.parsers.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.sun.tools.javac.util.List; +import io.lumigo.core.utils.AwsSdkV2Utils; +import io.lumigo.models.HttpSpan; +import java.util.Collections; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.services.dynamodb.model.*; + +class DynamoDBV2ParserTest { + + public static final String TABLE_NAME = "tableName"; + + private final DynamoDBV2Parser dynamoDBParser = new DynamoDBV2Parser(); + + private HttpSpan span; + + private Map item; + + private String itemHash; + + @BeforeEach + void setUp() { + span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); + item = Collections.singletonMap("k", AttributeValue.builder().s("v").build()); + itemHash = AwsSdkV2Utils.calculateItemHash(item); + } + + @Test + void test_parse_ddb_get_item() { + GetItemRequest request = GetItemRequest.builder().tableName(TABLE_NAME).build(); + GetItemResponse response = GetItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info(HttpSpan.Info.builder().resourceName(TABLE_NAME).build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_batch_get_item() { + BatchGetItemRequest request = + BatchGetItemRequest.builder() + .requestItems( + Collections.singletonMap( + TABLE_NAME, KeysAndAttributes.builder().build())) + .build(); + BatchGetItemResponse response = BatchGetItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info(HttpSpan.Info.builder().resourceName(TABLE_NAME).build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_put_item() { + PutItemRequest request = PutItemRequest.builder().tableName(TABLE_NAME).item(item).build(); + BatchGetItemResponse response = BatchGetItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info( + HttpSpan.Info.builder() + .resourceName(TABLE_NAME) + .messageId(itemHash) + .build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_update_item() { + UpdateItemRequest request = + UpdateItemRequest.builder().tableName(TABLE_NAME).key(item).build(); + UpdateItemResponse response = UpdateItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info( + HttpSpan.Info.builder() + .resourceName(TABLE_NAME) + .messageId(itemHash) + .build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_delete_item() { + DeleteItemRequest request = + DeleteItemRequest.builder().tableName(TABLE_NAME).key(item).build(); + DeleteItemResponse response = DeleteItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info( + HttpSpan.Info.builder() + .resourceName(TABLE_NAME) + .messageId(itemHash) + .build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_batch_write_item() { + BatchWriteItemRequest request = + BatchWriteItemRequest.builder() + .requestItems( + Collections.singletonMap( + TABLE_NAME, + List.of( + WriteRequest.builder() + .putRequest( + PutRequest.builder() + .item(item) + .build()) + .build()))) + .build(); + BatchWriteItemResponse response = BatchWriteItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.safeParse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info( + HttpSpan.Info.builder() + .resourceName(TABLE_NAME) + .messageId(itemHash) + .build()) + .build(); + assertEquals(span, expectedSpan); + } + + @Test + void test_parse_ddb_batch_delete_item() { + BatchWriteItemRequest request = + BatchWriteItemRequest.builder() + .requestItems( + Collections.singletonMap( + TABLE_NAME, + List.of( + WriteRequest.builder() + .deleteRequest( + DeleteRequest.builder() + .key(item) + .build()) + .build()))) + .build(); + BatchWriteItemResponse response = BatchWriteItemResponse.builder().build(); + Context.AfterExecution context = + InterceptorContext.builder().request(request).response(response).build(); + + dynamoDBParser.parse(span, context); + + HttpSpan expectedSpan = + HttpSpan.builder() + .info( + HttpSpan.Info.builder() + .resourceName(TABLE_NAME) + .messageId(itemHash) + .build()) + .build(); + assertEquals(span, expectedSpan); + } +} diff --git a/src/test/java/io/lumigo/core/parsers/v2/KinesisV2ParserTest.java b/src/test/java/io/lumigo/core/parsers/v2/KinesisV2ParserTest.java new file mode 100644 index 0000000..a3fddb8 --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v2/KinesisV2ParserTest.java @@ -0,0 +1,44 @@ +package io.lumigo.core.parsers.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.lumigo.models.HttpSpan; +import java.util.Arrays; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.services.kinesis.model.PutRecordsRequest; +import software.amazon.awssdk.services.kinesis.model.PutRecordsRequestEntry; +import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse; +import software.amazon.awssdk.services.kinesis.model.PutRecordsResultEntry; + +class KinesisV2ParserTest { + + private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); + KinesisV2Parser kinesisParser = new KinesisV2Parser(); + + @Test + void test_parse_kinesis_put_record_simple_flow_v2() { + PutRecordsRequest putRequest = + PutRecordsRequest.builder() + .records(PutRecordsRequestEntry.builder().build()) + .streamName("streamName") + .build(); + PutRecordsResponse putResponse = + PutRecordsResponse.builder() + .records( + PutRecordsResultEntry.builder() + .sequenceNumber("fee47356-6f6a-58c8-96dc-26d8aaa4631a") + .build()) + .build(); + Context.AfterExecution context = + InterceptorContext.builder().request(putRequest).response(putResponse).build(); + + kinesisParser.safeParse(span, context); + + HttpSpan expectedSpan = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); + expectedSpan.getInfo().setResourceName("streamName"); + expectedSpan.getInfo().setMessageIds(Arrays.asList("fee47356-6f6a-58c8-96dc-26d8aaa4631a")); + assertEquals(span, expectedSpan); + } +} diff --git a/src/test/java/io/lumigo/core/parsers/v2/SnsV2ParserTest.java b/src/test/java/io/lumigo/core/parsers/v2/SnsV2ParserTest.java new file mode 100644 index 0000000..81b3d4e --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v2/SnsV2ParserTest.java @@ -0,0 +1,34 @@ +package io.lumigo.core.parsers.v2; + +import static org.junit.jupiter.api.Assertions.*; + +import io.lumigo.models.HttpSpan; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.services.sns.model.PublishRequest; +import software.amazon.awssdk.services.sns.model.PublishResponse; + +class SnsV2ParserTest { + + private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); + SnsV2Parser SnsParser = new SnsV2Parser(); + + @Test + void test_parse_sns_with_full_details_v2() { + PublishRequest publishRequest = PublishRequest.builder().topicArn("topic").build(); + PublishResponse publishResponse = + PublishResponse.builder().messageId("fee47356-6f6a-58c8-96dc-26d8aaa4631a").build(); + Context.AfterExecution context = + InterceptorContext.builder() + .request(publishRequest) + .response(publishResponse) + .build(); + + SnsParser.safeParse(span, context); + + assertEquals("topic", span.getInfo().getResourceName()); + assertEquals("topic", span.getInfo().getTargetArn()); + assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); + } +} diff --git a/src/test/java/io/lumigo/core/parsers/v2/SqsV2ParserTest.java b/src/test/java/io/lumigo/core/parsers/v2/SqsV2ParserTest.java new file mode 100644 index 0000000..cf508c2 --- /dev/null +++ b/src/test/java/io/lumigo/core/parsers/v2/SqsV2ParserTest.java @@ -0,0 +1,36 @@ +package io.lumigo.core.parsers.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import io.lumigo.models.HttpSpan; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; +import software.amazon.awssdk.services.sqs.model.SendMessageResponse; + +class SqsV2ParserTest { + + private HttpSpan span = HttpSpan.builder().info(HttpSpan.Info.builder().build()).build(); + SqsV2Parser sqsParser = new SqsV2Parser(); + + @Test + void test_parse_sqs_with_full_details_v2() { + SendMessageRequest publishRequest = + SendMessageRequest.builder().queueUrl("queueUrl").build(); + SendMessageResponse messageResponse = + SendMessageResponse.builder() + .messageId("fee47356-6f6a-58c8-96dc-26d8aaa4631a") + .build(); + Context.AfterExecution context = + InterceptorContext.builder() + .request(publishRequest) + .response(messageResponse) + .build(); + + sqsParser.safeParse(span, context); + + assertEquals("queueUrl", span.getInfo().getResourceName()); + assertEquals("fee47356-6f6a-58c8-96dc-26d8aaa4631a", span.getInfo().getMessageId()); + } +} diff --git a/src/test/java/io/lumigo/core/utils/AwsSdkV2UtilsTests.java b/src/test/java/io/lumigo/core/utils/AwsSdkV2UtilsTests.java new file mode 100644 index 0000000..a7eb9bd --- /dev/null +++ b/src/test/java/io/lumigo/core/utils/AwsSdkV2UtilsTests.java @@ -0,0 +1,62 @@ +package io.lumigo.core.utils; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +public class AwsSdkV2UtilsTests { + + private Map attributeValueMap; + + @BeforeEach + void setUp() { + attributeValueMap = new HashMap<>(); + attributeValueMap.put("key", AttributeValue.builder().s("value").build()); + attributeValueMap.put("key2", AttributeValue.builder().n("2").build()); + attributeValueMap.put("key3", AttributeValue.builder().bool(true).build()); + attributeValueMap.put( + "key4", + AttributeValue.builder() + .m( + Collections.singletonMap( + "key", AttributeValue.builder().s("value4").build())) + .build()); + attributeValueMap.put( + "key5", + AttributeValue.builder() + .l( + AttributeValue.builder().s("value5").build(), + AttributeValue.builder().s("value5.2").build()) + .build()); + } + + @Test + void testCalculateItemHash() { + String result = AwsSdkV2Utils.calculateItemHash(attributeValueMap); + + assertEquals("10ac224af47748812c94ade5be937d59", result); + } + + @Test + void testConvertAttributeMapToSimpleMap() { + Map result = + AwsSdkV2Utils.convertAttributeMapToSimpleMap(attributeValueMap); + + assertEquals(attributeValueMap.size(), result.size()); + assertEquals("value", result.get("key")); + assertEquals("2", result.get("key2")); + assertEquals(true, result.get("key3")); + assertEquals(Collections.singletonMap("key", "value4"), result.get("key4")); + assertArrayEquals( + Lists.newArrayList("value5", "value5.2").toArray(), + ((ArrayList) result.get("key5")).toArray()); + } +}