Skip to content

Commit e8d8b73

Browse files
committed
✨ feat: add annotation saga #4
1 parent 6a1417f commit e8d8b73

File tree

8 files changed

+138
-24
lines changed

8 files changed

+138
-24
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter
9595
// enabling developers to write unit tests using Mockito's powerful mocking features.
9696
// It's designed to work specifically with the JUnit 5 platform, allowing for advanced testing capabilities.
9797
testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.12.4'
98+
// The "spring-boot-starter-aop" library, version 2.7.18, provides support for Aspect-Oriented Programming (AOP) in Spring Boot applications.
99+
// AOP enables modularization of cross-cutting concerns such as logging, security, and transactions by allowing aspects to be applied to various parts of the application.
100+
// This starter simplifies the setup and configuration of AOP-related functionality, promoting cleaner and more maintainable code by separating concerns effectively.
101+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.7.18'
98102
```
99103
100104
## Integration

plugin/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ dependencies {
187187
// enabling developers to write unit tests using Mockito's powerful mocking features.
188188
// It's designed to work specifically with the JUnit 5 platform, allowing for advanced testing capabilities.
189189
testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.12.4'
190+
// The "spring-boot-starter-aop" library, version 2.7.18, provides support for Aspect-Oriented Programming (AOP) in Spring Boot applications.
191+
// AOP enables modularization of cross-cutting concerns such as logging, security, and transactions by allowing aspects to be applied to various parts of the application.
192+
// This starter simplifies the setup and configuration of AOP-related functionality, promoting cleaner and more maintainable code by separating concerns effectively.
193+
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.7.18'
190194
}
191195

192196
test {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.clarify4j.config;
22

33
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.context.annotation.EnableAspectJAutoProxy;
45

56
@Configuration
7+
@EnableAspectJAutoProxy
68
public class Clarify4jConfig {
79
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.clarify4j.config.annotation;
2+
3+
import java.lang.annotation.*;
4+
5+
@Documented
6+
@Retention(RetentionPolicy.RUNTIME)
7+
@Target({ElementType.METHOD, ElementType.TYPE})
8+
public @interface Saga {
9+
String expression();
10+
11+
boolean disable() default false;
12+
13+
Class<?> clazz() default Saga.class;
14+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.clarify4j.config.handler;
2+
3+
import org.aspectj.lang.JoinPoint;
4+
import org.aspectj.lang.annotation.Aspect;
5+
import org.aspectj.lang.annotation.Before;
6+
import org.aspectj.lang.annotation.Pointcut;
7+
import org.aspectj.lang.reflect.MethodSignature;
8+
import org.clarify4j.config.annotation.Saga;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
import org.springframework.expression.EvaluationContext;
12+
import org.springframework.expression.ExpressionParser;
13+
import org.springframework.expression.common.TemplateParserContext;
14+
import org.springframework.expression.spel.standard.SpelExpressionParser;
15+
import org.springframework.expression.spel.support.StandardEvaluationContext;
16+
import org.springframework.stereotype.Component;
17+
import org.unify4j.common.Object4j;
18+
19+
import java.lang.reflect.Method;
20+
21+
@Aspect
22+
@Component
23+
public class SagaHandler {
24+
protected static Logger logger = LoggerFactory.getLogger(SagaHandler.class);
25+
protected static final TemplateParserContext TEMPLATE_PARSER_CONTEXT = new TemplateParserContext("{{", "}}");
26+
protected static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
27+
28+
@Pointcut(value = "@annotation(org.clarify4j.config.annotation.Saga)")
29+
public void controller() {
30+
}
31+
32+
@Before(value = "controller()")
33+
public void handle(JoinPoint joinPoint) throws Throwable {
34+
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
35+
Method method = signature.getMethod();
36+
Saga saga = method.getAnnotation(Saga.class);
37+
if (saga.disable()) {
38+
return;
39+
}
40+
EvaluationContext context = new StandardEvaluationContext();
41+
Object[] args = joinPoint.getArgs();
42+
String[] parameters = signature.getParameterNames();
43+
44+
for (int i = 0; i < args.length; i++) {
45+
context.setVariable(parameters[i], args[i]);
46+
}
47+
String value = EXPRESSION_PARSER.parseExpression(saga.expression(), TEMPLATE_PARSER_CONTEXT).getValue(context, String.class);
48+
if (Object4j.allNotNull(saga.clazz())) {
49+
logger = LoggerFactory.getLogger(saga.clazz());
50+
}
51+
logger.info(value);
52+
}
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.clarify4j;
2+
3+
import org.aspectj.lang.JoinPoint;
4+
import org.aspectj.lang.reflect.MethodSignature;
5+
import org.clarify4j.config.annotation.Saga;
6+
import org.clarify4j.config.handler.SagaHandler;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
import org.mockito.InjectMocks;
10+
import org.mockito.Mock;
11+
import org.mockito.MockitoAnnotations;
12+
13+
import java.lang.reflect.Method;
14+
15+
import static org.mockito.Mockito.*;
16+
17+
public class SagaAspectTest {
18+
@Mock
19+
private JoinPoint joinPoint;
20+
21+
@Mock
22+
private MethodSignature methodSignature;
23+
24+
@InjectMocks
25+
private SagaHandler sagaHandler;
26+
27+
@BeforeEach
28+
public void setUp() {
29+
MockitoAnnotations.openMocks(this);
30+
}
31+
32+
@Test
33+
public void testSaga() throws Throwable {
34+
// Create a Method instance representing the method to be tested
35+
Method method = TestMethods.class.getMethod("updateUser", Long.class, String.class);
36+
37+
// Mock method signature and annotation
38+
when(joinPoint.getSignature()).thenReturn(methodSignature);
39+
when(methodSignature.getMethod()).thenReturn(method);
40+
41+
// Mock method arguments
42+
when(joinPoint.getArgs()).thenReturn(new Object[]{1L, "John Doe"});
43+
when(methodSignature.getParameterNames()).thenReturn(new String[]{"userId", "newName"});
44+
45+
// Call the aspect method
46+
sagaHandler.handle(joinPoint);
47+
48+
// Verify that the logging occurred (you might mock the logging framework to assert this)
49+
// Here, we are just verifying that the method ran without exceptions
50+
verify(joinPoint, times(1)).getSignature();
51+
}
52+
53+
// Helper class to provide the method for reflection
54+
public static class TestMethods {
55+
@Saga(expression = "User {{#userId}} is being updated with the name {{#newName}}")
56+
public void updateUser(Long userId, String newName) {
57+
// This method is just a placeholder for reflection
58+
}
59+
}
60+
}

plugin/src/test/groovy/org/clarify4j/Wizard4jPluginTest.groovy

Lines changed: 0 additions & 23 deletions
This file was deleted.

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
* For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.6/userguide/multi_project_builds.html in the Gradle documentation.
66
*/
77

8-
rootProject.name = 'wizards2s4j'
8+
rootProject.name = 'clarify4j'
99
include('plugin')

0 commit comments

Comments
 (0)