Skip to content

Commit

Permalink
feat: add sns producer and consumer annotation (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
kayman-mk committed Jun 9, 2022
1 parent 9a37e2b commit 835b032
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 6 deletions.
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);
}
}

0 comments on commit 835b032

Please sign in to comment.