Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

feat: add sns producer and consumer annotation #38

Merged
merged 2 commits into from Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -9,7 +9,7 @@ This project contains some API classes to allow users to define their own scanne
<dependency>
<groupId>com.hlag.tools.commvis</groupId>
<artifactId>api</artifactId>
<version>2.5.5</version>
<version>2.7.0</version>
<scope>provided</scope>
</dependency>
```
Expand Down
@@ -0,0 +1,26 @@
package com.hlag.tools.commvis.analyzer.annotation;

import java.lang.annotation.*;

/**
* Marks a producer for AWS SNS messages.
*/
@Repeatable(VisualizeSnsProducers.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisualizeSnsProducer {
/**
* @return name of the SNS topic messages are sent to
*/
String topicName();

/**
* @return the id of the project called, usually the Gitlab project id or similar
*/
String projectId();

/**
* @return the name of the project called. Just for a better visibility in the code. The value isn't used.
*/
String projectName() default "";
}
@@ -0,0 +1,18 @@
package com.hlag.tools.commvis.analyzer.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Used to group multiple {@link VisualizeSnsProducer} annotations on one element.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisualizeSnsProducers {
/**
* @return all grouped {@link VisualizeSnsProducer} annotations
*/
VisualizeSnsProducer[] value();
}
@@ -1,13 +1,12 @@
package com.hlag.tools.commvis.analyzer.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;

/**
* Annotated on methods to indicate that SQS messages are consumed.
* Annotated on methods to indicate that SQS messages are consumed. If the messages are received via a SNS topic
* subscription use the {@link VisualizeSqsViaSnsConsumer} instead.
*/
@Repeatable(VisualizeSqsConsumers.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisualizeSqsConsumer {
Expand Down
Expand Up @@ -3,9 +3,14 @@
import com.google.gson.annotations.SerializedName;
import com.hlag.tools.commvis.analyzer.model.AbstractCommunicationModelVisitor;

import java.lang.annotation.*;

/**
* Marks a producer for AWS SQS messages.
*/
@Repeatable(VisualizeSqsProducers.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisualizeSqsProducer {
/**
* @return name of the SQS queue messages are sent to
Expand Down
@@ -0,0 +1,21 @@
package com.hlag.tools.commvis.analyzer.annotation;

import java.lang.annotation.*;

/**
* Annotated on methods to indicate that SQS messages are consumed from a SBS topic.
*/
@Repeatable(VisualizeSqsViaSnsConsumers.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisualizeSqsViaSnsConsumer {
/**
* @return name of the SNS topic messages are received from (via subscription)
*/
String topicName();

/**
* @return the name of the project the messages are received from. Just for a better visibility in the code. The value isn't used.
*/
String projectName() default "";
}
@@ -0,0 +1,18 @@
package com.hlag.tools.commvis.analyzer.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
* Used to group multiple {@link VisualizeSqsViaSnsConsumer} annotations on one element.
*/
public @interface VisualizeSqsViaSnsConsumers {
/**
* @return all grouped {@link VisualizeSqsViaSnsConsumer} annotations
*/
VisualizeSqsViaSnsConsumer[] value();
}
Expand Up @@ -12,5 +12,8 @@ public abstract class AbstractCommunicationModelVisitor {
public abstract void visit(JmsReceiver jmsReceiver);

public abstract void visit(SqsConsumer sqsConsumer);
public abstract void visit(SqsViaSnsConsumer sqsViaSnsConsumer);
public abstract void visit(SqsProducer sqsProducer);

public abstract void visit(SnsProducer snsProducer);
}
Expand Up @@ -60,12 +60,24 @@ public class CommunicationModel {
@SerializedName(value = "sqs_consumers")
private Collection<SqsConsumer> sqsConsumers = new HashSet<>();

/**
* All SQS via SNS consumers.
*/
@SerializedName(value = "sqs_via_sns_consumers")
private Collection<SqsViaSnsConsumer> sqsViaSnsConsumers = new HashSet<>();

/**
* All SQS producers.
*/
@SerializedName(value = "sqs_producers")
private Collection<SqsProducer> sqsProducers = new HashSet<>();

/**
* All SNS producers.
*/
@SerializedName(value = "sns_producers")
private Collection<SnsProducer> snsProducers = new HashSet<>();

private CommunicationModel() {
// for GSON deserialize
projectId = NOT_SET;
Expand All @@ -84,6 +96,10 @@ public <T extends ISenderReceiverCommunication> void addSenderReceiver(T endpoin
sqsConsumers.add((SqsConsumer) endpoint);
} else if (endpoint instanceof SqsProducer) {
sqsProducers.add((SqsProducer) endpoint);
} else if (endpoint instanceof SnsProducer) {
snsProducers.add((SnsProducer) endpoint);
} else if (endpoint instanceof SqsViaSnsConsumer) {
sqsViaSnsConsumers.add((SqsViaSnsConsumer) endpoint);
} else {
throw new IllegalStateException(String.format("We have no endpoints of type %s", endpoint.getClass().getCanonicalName()));
}
Expand All @@ -97,5 +113,7 @@ public void visit(AbstractCommunicationModelVisitor visitor) {
jmsConsumers.forEach(e -> e.visit(visitor));
sqsConsumers.forEach(e -> e.visit(visitor));
sqsProducers.forEach(e -> e.visit(visitor));
snsProducers.forEach(e -> e.visit(visitor));
sqsViaSnsConsumers.forEach(e -> e.visit(visitor));
}
}
@@ -1,9 +1,13 @@
package com.hlag.tools.commvis.analyzer.model;

import com.hlag.tools.commvis.analyzer.annotation.VisualizeSnsProducer;
import com.hlag.tools.commvis.analyzer.annotation.VisualizeSqsViaSnsConsumer;
import com.hlag.tools.commvis.analyzer.port.IIdentityGenerator;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@RequiredArgsConstructor
public class EndpointFactory {
Expand All @@ -25,7 +29,15 @@ public SqsConsumer createSqsReceiver(String className, String methodName, String
return new SqsConsumer(className, methodName, queueName, identityGenerator.generateUniqueId());
}

public SqsViaSnsConsumer createSqsViaSnsConsumer(VisualizeSqsViaSnsConsumer annotation, Method method) {
return new SqsViaSnsConsumer(method.getDeclaringClass().getCanonicalName(), method.getName(), annotation.topicName(), identityGenerator.generateUniqueId());
}

public SqsProducer createSqsProducer(String className, String methodName, String queueName, String destinationProjectId) {
return new SqsProducer(className, methodName, queueName, destinationProjectId, identityGenerator.generateUniqueId());
}

public SnsProducer createSnsProducer(VisualizeSnsProducer annotation, Method method) {
return new SnsProducer(method.getDeclaringClass().getCanonicalName(), method.getName(), annotation.topicName(), annotation.projectId(), identityGenerator.generateUniqueId());
}
}
@@ -0,0 +1,48 @@
package com.hlag.tools.commvis.analyzer.model;

import com.google.gson.annotations.SerializedName;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Value;

/**
* A producer for SNS messages.
*/
@Value
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class SnsProducer implements ISenderReceiverCommunication, IProducer {
/**
* the class name where the producer was found.
*/
@SerializedName(value="class_name")
String className;

/**
* the method name were the producer was found.
*/
@SerializedName(value="method_name")
String methodName;

/**
* the topic the messages are sent to.
*/
@SerializedName(value="topic_name")
String topicName;

/**
* The project id of the referenced project.
*/
@SerializedName(value="destination_project_id")
String destinationProjectId;

/**
* internal id of this node
*/
@SerializedName(value="id")
String id;

@Override
public void visit(AbstractCommunicationModelVisitor visitor) {
visitor.visit(this);
}
}
@@ -0,0 +1,41 @@
package com.hlag.tools.commvis.analyzer.model;

import com.google.gson.annotations.SerializedName;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Value;

/**
* A receiver for SQS messages via SNS topics.
*/
@Value
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class SqsViaSnsConsumer implements ISenderReceiverCommunication, IConsumer {
@SerializedName(value="class_name")
String className;

@SerializedName(value="method_name")
String methodName;

@SerializedName(value="topic_name")
String topicName;

@SerializedName(value="id")
String id;

@Override
public void visit(AbstractCommunicationModelVisitor visitor) {
visitor.visit(this);
}

@Override
public boolean isProducedBy(IProducer producer) {
if (producer instanceof SnsProducer) {
SnsProducer snsProducer = (SnsProducer) producer;

return topicName.equals(snsProducer.getTopicName());
}

return false;
}
}
@@ -1,5 +1,7 @@
package com.hlag.tools.commvis.analyzer.model;

import com.hlag.tools.commvis.analyzer.annotation.VisualizeSnsProducer;
import com.hlag.tools.commvis.analyzer.annotation.VisualizeSqsViaSnsConsumer;
import com.hlag.tools.commvis.analyzer.port.IIdentityGenerator;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -12,6 +14,14 @@
class EndpointFactoryTest {
private static final String FIXED_ID = "MY-UNIQUE-ID";

private static class TestProducersAndConsumers {
@VisualizeSnsProducer(topicName = "topic", projectId = "4711")
public void produceSnsMessage() {}

@VisualizeSqsViaSnsConsumer(topicName = "topic1", projectName = "4712")
public void consumeSqsViaSnsMessage() {}
}

@Mock
private IIdentityGenerator identityGenerator;
@InjectMocks
Expand Down Expand Up @@ -78,4 +88,25 @@ void shouldSetAllFields_whenCreateSqsProducer() {
Assertions.assertThat(actualSqsProducer.getDestinationProjectId()).isEqualTo("destinationProjectId");
Assertions.assertThat(actualSqsProducer.getId()).isEqualTo(FIXED_ID);
}

@Test
void shouldSetAllFields_whenCreateSnsProducer() throws NoSuchMethodException {
SnsProducer actualSnsProducer = factory.createSnsProducer(TestProducersAndConsumers.class.getDeclaredMethod("produceSnsMessage").getAnnotationsByType(VisualizeSnsProducer.class)[0], TestProducersAndConsumers.class.getDeclaredMethod("produceSnsMessage"));

Assertions.assertThat(actualSnsProducer.getClassName()).isEqualTo("com.hlag.tools.commvis.analyzer.model.EndpointFactoryTest.TestProducersAndConsumers");
Assertions.assertThat(actualSnsProducer.getMethodName()).isEqualTo("produceSnsMessage");
Assertions.assertThat(actualSnsProducer.getTopicName()).isEqualTo("topic");
Assertions.assertThat(actualSnsProducer.getDestinationProjectId()).isEqualTo("4711");
Assertions.assertThat(actualSnsProducer.getId()).isEqualTo(FIXED_ID);
}

@Test
void shouldSetAllFields_whenCreateSqsViaSnsConsumer() throws NoSuchMethodException {
SqsViaSnsConsumer actualSqsViaSnsConsumer = factory.createSqsViaSnsConsumer(TestProducersAndConsumers.class.getDeclaredMethod("consumeSqsViaSnsMessage").getAnnotationsByType(VisualizeSqsViaSnsConsumer.class)[0], TestProducersAndConsumers.class.getDeclaredMethod("consumeSqsViaSnsMessage"));

Assertions.assertThat(actualSqsViaSnsConsumer.getClassName()).isEqualTo("com.hlag.tools.commvis.analyzer.model.EndpointFactoryTest.TestProducersAndConsumers");
Assertions.assertThat(actualSqsViaSnsConsumer.getMethodName()).isEqualTo("consumeSqsViaSnsMessage");
Assertions.assertThat(actualSqsViaSnsConsumer.getTopicName()).isEqualTo("topic1");
Assertions.assertThat(actualSqsViaSnsConsumer.getId()).isEqualTo(FIXED_ID);
}
}