diff --git a/README.md b/README.md index 296c7996..ce30afc2 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ To use the plugin you need Gradle version 5.6 or later, to start add the followi ```groovy plugins { - id "co.com.bancolombia.cleanArchitecture" version "1.8.4" + id "co.com.bancolombia.cleanArchitecture" version "1.8.5" } ``` @@ -185,7 +185,9 @@ The Scaffolding Clean Architecture plugin will allow you run 8 tasks: | asynceventbus | Async Event Bus | | | restconsumer | Rest Client Consumer | --url [url] | | redis | Redis | --mode [template-repository] --secret [true-false] | + | rsocket | Rsocket Requester | | | r2dbc | R2dbc Postgresql Client | | + _**This task will generate something like that:**_ ```bash @@ -226,6 +228,7 @@ The Scaffolding Clean Architecture plugin will allow you run 8 tasks: | generic | Empty Entry Point | --name [name] | | restmvc | API REST (Spring Boot Starter Web) | --server [serverOption] default undertow | | webflux | API REST (Spring Boot Starter WebFlux) | --router [true, false] default true | + | rsocket | Rsocket Controller Entry Point | | Additionally, if you'll use a restmvc, you can specify the web server on which the application will run. By default, undertow. diff --git a/gradle.properties b/gradle.properties index 13c8be71..d46c43b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ package=co.com.bancolombia -systemProp.version=1.8.4 +systemProp.version=1.8.5 \ No newline at end of file diff --git a/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java b/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java index bb784a3e..7f9022ee 100644 --- a/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java +++ b/src/functionalTest/java/co/com/bancolombia/PluginCleanFunctionalTest.java @@ -259,6 +259,37 @@ public void canRunTaskGenerateDrivenAdapterRestConsumerCaseWithParameters() { assertEquals(result.task(":" + task).getOutcome(), TaskOutcome.SUCCESS); } + @Test + public void canRunTaskGenerateDrivenAdapterRsocketRequesterCase() { + canRunTaskGenerateStructureReactiveProject(); + String task = "generateDrivenAdapter"; + String valueDrivenAdapter = "rsocket"; + + runner.withArguments(task, "--type=" + valueDrivenAdapter); + runner.withProjectDir(projectDir); + BuildResult result = runner.build(); + assertTrue(new File("build/functionalTest/infrastructure/driven-adapters/rsocket-requester/build.gradle").exists()); + assertTrue(new File("build/functionalTest/applications/app-service/src/main/java/co/com/bancolombia/config/RequesterConfig.java").exists()); + assertTrue(new File("build/functionalTest/infrastructure/driven-adapters/rsocket-requester/src/main/java/co/com/bancolombia/service/RsocketAdapter.java").exists()); + assertTrue(new File("build/functionalTest/infrastructure/driven-adapters/rsocket-requester/src/test/java/co/com/bancolombia/service").exists()); + assertEquals(result.task(":" + task).getOutcome(), TaskOutcome.SUCCESS); + } + + @Test + public void canRunTaskGenerateEntryPointrRsocketResponderCase() { + canRunTaskGenerateStructureReactiveProject(); + String task = "generateEntryPoint"; + String valueDrivenAdapter = "rsocket"; + + runner.withArguments(task, "--type=" + valueDrivenAdapter); + runner.withProjectDir(projectDir); + BuildResult result = runner.build(); + assertTrue(new File("build/functionalTest/infrastructure/entry-points/rsocket-responder/build.gradle").exists()); + assertTrue(new File("build/functionalTest/infrastructure/entry-points/rsocket-responder/src/main/java/co/com/bancolombia/controller/RsocketController.java").exists()); + assertTrue(new File("build/functionalTest/infrastructure/entry-points/rsocket-responder/src/test/java/co/com/bancolombia/controller").exists()); + assertEquals(result.task(":" + task).getOutcome(), TaskOutcome.SUCCESS); + } + @Test public void canRunTaskGenerateEntryPointCaseWithParameters() { canRunTaskGenerateStructureWithOutParameters(); diff --git a/src/main/java/co/com/bancolombia/Constants.java b/src/main/java/co/com/bancolombia/Constants.java index f14ae24f..1e2a2c4a 100644 --- a/src/main/java/co/com/bancolombia/Constants.java +++ b/src/main/java/co/com/bancolombia/Constants.java @@ -16,8 +16,7 @@ public class Constants { public static final String SECRETS_VERSION = "2.1.0"; public static final String RCOMMONS_ASYNC_COMMONS_STARTER_VERSION = "0.4.7"; public static final String RCOMMONS_OBJECT_MAPPER_VERSION = "0.1.0"; - public static final String PLUGIN_VERSION = "1.8.4"; - + public static final String PLUGIN_VERSION = "1.8.5"; public static final String TOMCAT_EXCLUSION = "compile.exclude group: \"org.springframework.boot\", module:\"spring-boot-starter-tomcat\""; public enum BooleanOption { diff --git a/src/main/java/co/com/bancolombia/factory/adapters/DrivenAdapterRsocketRequester.java b/src/main/java/co/com/bancolombia/factory/adapters/DrivenAdapterRsocketRequester.java new file mode 100644 index 00000000..a6303090 --- /dev/null +++ b/src/main/java/co/com/bancolombia/factory/adapters/DrivenAdapterRsocketRequester.java @@ -0,0 +1,25 @@ +package co.com.bancolombia.factory.adapters; + +import co.com.bancolombia.exceptions.CleanException; +import co.com.bancolombia.exceptions.InvalidTaskOptionException; +import co.com.bancolombia.factory.ModuleBuilder; +import co.com.bancolombia.factory.ModuleFactory; + +import java.io.IOException; + +public class DrivenAdapterRsocketRequester implements ModuleFactory { + + @Override + public void buildModule(ModuleBuilder builder) throws IOException, CleanException { + builder.loadPackage(); + if (builder.isReactive()) { + builder.appendToSettings("rsocket-requester", "infrastructure/driven-adapters"); + builder.appendDependencyToModule("app-service", + "implementation project(':rsocket-requester')"); + builder.setupFromTemplate("driven-adapter/rsocket-requester"); + } else { + throw new InvalidTaskOptionException("Rsocket requester Driven Adapter is only available in reactive projects"); + } + + } +} diff --git a/src/main/java/co/com/bancolombia/factory/adapters/ModuleFactoryDrivenAdapter.java b/src/main/java/co/com/bancolombia/factory/adapters/ModuleFactoryDrivenAdapter.java index fb26168a..f9b28583 100644 --- a/src/main/java/co/com/bancolombia/factory/adapters/ModuleFactoryDrivenAdapter.java +++ b/src/main/java/co/com/bancolombia/factory/adapters/ModuleFactoryDrivenAdapter.java @@ -19,6 +19,8 @@ public static ModuleFactory getDrivenAdapterFactory(DrivenAdapterType type) thro return new DrivenAdapterRestConsumer(); case REDIS: return new DrivenAdapterRedis(); + case RSOCKET: + return new DrivenAdapterRsocketRequester(); case R2DBC: return new DrivenAdapterR2dbcPostgreSQL(); default: @@ -27,6 +29,6 @@ public static ModuleFactory getDrivenAdapterFactory(DrivenAdapterType type) thro } public enum DrivenAdapterType { - JPA, MONGODB, ASYNCEVENTBUS, GENERIC, RESTCONSUMER, REDIS, R2DBC + JPA, MONGODB, ASYNCEVENTBUS, GENERIC, RESTCONSUMER, REDIS, RSOCKET, R2DBC } } diff --git a/src/main/java/co/com/bancolombia/factory/entrypoints/EntryPointRsocketResponder.java b/src/main/java/co/com/bancolombia/factory/entrypoints/EntryPointRsocketResponder.java new file mode 100644 index 00000000..3dcf01fb --- /dev/null +++ b/src/main/java/co/com/bancolombia/factory/entrypoints/EntryPointRsocketResponder.java @@ -0,0 +1,28 @@ +package co.com.bancolombia.factory.entrypoints; + +import co.com.bancolombia.exceptions.CleanException; +import co.com.bancolombia.exceptions.InvalidTaskOptionException; +import co.com.bancolombia.factory.ModuleBuilder; +import co.com.bancolombia.factory.ModuleFactory; + +import java.io.IOException; + +public class EntryPointRsocketResponder implements ModuleFactory { + + @Override + public void buildModule(ModuleBuilder builder) throws IOException, CleanException { + builder.loadPackage(); + if (builder.isReactive()) { + builder.appendToSettings("rsocket-responder", "infrastructure/entry-points"); + builder.appendToProperties("spring.rsocket.server") + .put("port", 7000); + builder.appendDependencyToModule("app-service", + "implementation project(':rsocket-responder')"); + builder.setupFromTemplate("entry-point/rsocket-responder"); + } else { + throw new InvalidTaskOptionException("Rsocket responder Entry Point is only available in reactive projects"); + } + + } + +} diff --git a/src/main/java/co/com/bancolombia/factory/entrypoints/ModuleFactoryEntryPoint.java b/src/main/java/co/com/bancolombia/factory/entrypoints/ModuleFactoryEntryPoint.java index 5b6dfb95..f0f28e2f 100644 --- a/src/main/java/co/com/bancolombia/factory/entrypoints/ModuleFactoryEntryPoint.java +++ b/src/main/java/co/com/bancolombia/factory/entrypoints/ModuleFactoryEntryPoint.java @@ -13,12 +13,14 @@ public static ModuleFactory getEntryPointFactory(EntryPointType type) throws Inv return new EntryPointRestWebflux(); case GENERIC: return new EntryPointGeneric(); + case RSOCKET: + return new EntryPointRsocketResponder(); default: throw new InvalidTaskOptionException("Entry Point type invalid"); } } public enum EntryPointType { - RESTMVC, WEBFLUX, GENERIC + RESTMVC, WEBFLUX, GENERIC, RSOCKET } } diff --git a/src/main/resources/driven-adapter/rsocket-requester/build.gradle.mustache b/src/main/resources/driven-adapter/rsocket-requester/build.gradle.mustache new file mode 100644 index 00000000..72c8dd09 --- /dev/null +++ b/src/main/resources/driven-adapter/rsocket-requester/build.gradle.mustache @@ -0,0 +1,6 @@ +dependencies { + implementation project(':model') + implementation project(':usecase') + implementation 'org.springframework:spring-context' + compile 'org.springframework.boot:spring-boot-starter-rsocket' +} \ No newline at end of file diff --git a/src/main/resources/driven-adapter/rsocket-requester/config/requester-config.java.mustache b/src/main/resources/driven-adapter/rsocket-requester/config/requester-config.java.mustache new file mode 100644 index 00000000..2e2e95bc --- /dev/null +++ b/src/main/resources/driven-adapter/rsocket-requester/config/requester-config.java.mustache @@ -0,0 +1,26 @@ +package {{package}}.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.codec.cbor.Jackson2CborDecoder; +import org.springframework.http.codec.cbor.Jackson2CborEncoder; +import org.springframework.messaging.rsocket.RSocketRequester; +import org.springframework.messaging.rsocket.RSocketStrategies; + +@Configuration +public class RequesterConfig { + + @Bean + public RSocketRequester rSocketRequester(RSocketStrategies rSocketStrategies) { + RSocketStrategies strategies = RSocketStrategies.builder() + .encoders(encoders -> encoders.add(new Jackson2CborEncoder())) + .decoders(decoders -> decoders.add(new Jackson2CborDecoder())) + .build(); + return RSocketRequester.builder() + .rsocketStrategies(strategies) + .dataMimeType(MediaType.APPLICATION_CBOR) + .tcp("localhost",7000); // server IP or DNS, and port + } + +} diff --git a/src/main/resources/driven-adapter/rsocket-requester/definition.json b/src/main/resources/driven-adapter/rsocket-requester/definition.json new file mode 100644 index 00000000..faab4019 --- /dev/null +++ b/src/main/resources/driven-adapter/rsocket-requester/definition.json @@ -0,0 +1,11 @@ +{ + "folders": [ + "infrastructure/driven-adapters/rsocket-requester/src/main/java/{{packagePath}}/service", + "infrastructure/driven-adapters/rsocket-requester/src/test/java/{{packagePath}}/service" + ], + "files": { + "driven-adapter/rsocket-requester/config/requester-config.java.mustache": "applications/app-service/src/main/java/{{packagePath}}/config/RequesterConfig.java", + "driven-adapter/rsocket-requester/build.gradle.mustache": "infrastructure/driven-adapters/rsocket-requester/build.gradle", + "driven-adapter/rsocket-requester/rsocket-requester.java.mustache": "infrastructure/driven-adapters/rsocket-requester/src/main/java/{{packagePath}}/service/RsocketAdapter.java" + } +} diff --git a/src/main/resources/driven-adapter/rsocket-requester/rsocket-requester.java.mustache b/src/main/resources/driven-adapter/rsocket-requester/rsocket-requester.java.mustache new file mode 100644 index 00000000..01f55e25 --- /dev/null +++ b/src/main/resources/driven-adapter/rsocket-requester/rsocket-requester.java.mustache @@ -0,0 +1,60 @@ +package {{package}}.service; + +{{#lombok}} +import lombok.RequiredArgsConstructor; +{{/lombok}} +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import org.springframework.messaging.rsocket.RSocketRequester; + +@Service +{{#lombok}} +@RequiredArgsConstructor +{{/lombok}} +public class RsocketAdapter // implements Gateway from domain +{ + + private final RSocketRequester rSocketRequester; + {{^lombok}} + + public RsocketAdapter(RSocketRequester rSocketRequester) { + this.rSocketRequester = rSocketRequester; + } + {{/lombok}} + + // interaction model Request/Response + public Mono callRouteRequest(Object objRequest/* change for object request */) { + return this.rSocketRequester + .route("route.request.response") + .data(objRequest) + .retrieveMono(Object.class) + .log(); + } + + // interaction model Fire-and-Forget + public Mono callRouteFireForget(Object objRequest/* change for object request */) { + return this.rSocketRequester + .route("route.fire.forget") + .data(objRequest) + .send() + .log(); + } + + // interaction model Request/Stream + public Flux callRouteRequestStream() { + return this.rSocketRequester + .route("route.request.stream") + .retrieveFlux(Object.class) + .log(); + } + + // interaction model Channel + public Flux callRouteChannel(Flux objRequest) { + return this.rSocketRequester + .route("route.channel") + .data(objRequest) + .retrieveFlux(Object.class) + .log(); + } +} \ No newline at end of file diff --git a/src/main/resources/entry-point/rsocket-responder/build.gradle.mustache b/src/main/resources/entry-point/rsocket-responder/build.gradle.mustache new file mode 100644 index 00000000..25fcd608 --- /dev/null +++ b/src/main/resources/entry-point/rsocket-responder/build.gradle.mustache @@ -0,0 +1,6 @@ +dependencies { + implementation project(':model') + implementation project(':usecase') + implementation 'org.springframework:spring-context' + implementation 'org.springframework.boot:spring-boot-starter-rsocket' +} diff --git a/src/main/resources/entry-point/rsocket-responder/definition.json b/src/main/resources/entry-point/rsocket-responder/definition.json new file mode 100644 index 00000000..a8ca2171 --- /dev/null +++ b/src/main/resources/entry-point/rsocket-responder/definition.json @@ -0,0 +1,10 @@ +{ + "folders": [ + "infrastructure/entry-points/rsocket-responder/src/main/java/{{packagePath}}/controller", + "infrastructure/entry-points/rsocket-responder/src/test/java/{{packagePath}}/controller" + ], + "files": { + "entry-point/rsocket-responder/build.gradle.mustache": "infrastructure/entry-points/rsocket-responder/build.gradle", + "entry-point/rsocket-responder/rsocket-responder.java.mustache": "infrastructure/entry-points/rsocket-responder/src/main/java/{{packagePath}}/controller/RsocketController.java" + } +} diff --git a/src/main/resources/entry-point/rsocket-responder/rsocket-responder.java.mustache b/src/main/resources/entry-point/rsocket-responder/rsocket-responder.java.mustache new file mode 100644 index 00000000..459acaf8 --- /dev/null +++ b/src/main/resources/entry-point/rsocket-responder/rsocket-responder.java.mustache @@ -0,0 +1,53 @@ +package {{package}}.controller; + +{{#lombok}} +import lombok.RequiredArgsConstructor; +{{/lombok}} +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.stereotype.Controller; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Controller +{{#lombok}} +@RequiredArgsConstructor +{{/lombok}} +public class RsocketController { + + // private final MyUseCase useCase; +{{^lombok}} + + /*public RsocketController(MyUseCase useCase) { + this.useCase = useCase; + }*/ +{{/lombok}} + + // interaction model Request/Response + @MessageMapping(value = "route.request.response") + public Mono getRequestResponse(Object objRequest/* change for object request */) { + // return useCase.doAction(); + return Mono.empty(); + } + + // interaction model Request/Stream + @MessageMapping(value = "route.request.stream") + public Flux getRequestStream() { + // return useCase.doAction(); + return Flux.empty(); + } + + // interaction model Fire-and-Forget + @MessageMapping(value = "route.fire.forget") + public Mono getRequetsFireForget(Object objRequest/* change for object request */) { + // return useCase.doAction(objRequest); + return Mono.empty(); + } + + // interaction model Channel + @MessageMapping(value = "route.channel") + public Flux getChannel(Flux objRequest) { + // return useCase.doAction(objRequest); + return Flux.empty(); + } + +} \ No newline at end of file diff --git a/src/test/java/co/com/bancolombia/task/GenerateDrivenAdapterTaskTest.java b/src/test/java/co/com/bancolombia/task/GenerateDrivenAdapterTaskTest.java index b867aa6e..20608c46 100644 --- a/src/test/java/co/com/bancolombia/task/GenerateDrivenAdapterTaskTest.java +++ b/src/test/java/co/com/bancolombia/task/GenerateDrivenAdapterTaskTest.java @@ -100,6 +100,20 @@ public void generateRestConsumer() throws IOException, CleanException { assertTrue(new File("build/unitTest/applications/app-service/src/main/java/co/com/bancolombia/config/RestConsumerConfig.java").exists()); } + @Test + public void generateRsocketRequester() throws IOException, CleanException { + // Arrange + setup(GenerateStructureTask.ProjectType.REACTIVE); + task.setType(ModuleFactoryDrivenAdapter.DrivenAdapterType.RSOCKET); + // Act + task.generateDrivenAdapterTask(); + // Assert + assertTrue(new File("build/unitTest/infrastructure/driven-adapters/rsocket-requester/build.gradle").exists()); + assertTrue(new File("build/unitTest/applications/app-service/src/main/java/co/com/bancolombia/config/RequesterConfig.java").exists()); + assertTrue(new File("build/unitTest/infrastructure/driven-adapters/rsocket-requester/src/main/java/co/com/bancolombia/service/RsocketAdapter.java").exists()); + assertTrue(new File("build/unitTest/infrastructure/driven-adapters/rsocket-requester/src/test/java/co/com/bancolombia/service").exists()); + } + @Test public void generateDrivenAdapterJPARepository() throws IOException, CleanException { // Arrange diff --git a/src/test/java/co/com/bancolombia/task/GenerateEntryPointTaskTest.java b/src/test/java/co/com/bancolombia/task/GenerateEntryPointTaskTest.java index 2f5e288b..a3acad38 100644 --- a/src/test/java/co/com/bancolombia/task/GenerateEntryPointTaskTest.java +++ b/src/test/java/co/com/bancolombia/task/GenerateEntryPointTaskTest.java @@ -27,11 +27,16 @@ public class GenerateEntryPointTaskTest { private GenerateEntryPointTask task; @Before - public void setup() throws IOException, CleanException { + public void init() throws IOException, CleanException { + setup(GenerateStructureTask.ProjectType.IMPERATIVE); + } + + public void setup(GenerateStructureTask.ProjectType type) throws IOException, CleanException { Project project = ProjectBuilder.builder().withProjectDir(new File("build/unitTest")).build(); deleteStructure(project.getProjectDir().toPath()); project.getTasks().create("ca", GenerateStructureTask.class); GenerateStructureTask caTask = (GenerateStructureTask) project.getTasks().getByName("ca"); + caTask.setType(type); caTask.generateStructureTask(); ProjectBuilder.builder() @@ -106,6 +111,19 @@ public void generateEntryPointGeneric() throws IOException, CleanException { assertTrue(new File("build/unitTest/infrastructure/entry-points/my-entry-point/src/test/java/co/com/bancolombia/myentrypoint").exists()); } + @Test + public void generateEntryPointRsocketResponder() throws IOException, CleanException { + // Arrange + setup(GenerateStructureTask.ProjectType.REACTIVE); + task.setType(ModuleFactoryEntryPoint.EntryPointType.RSOCKET); + // Act + task.generateEntryPointTask(); + // Assert + assertTrue(new File("build/unitTest/infrastructure/entry-points/rsocket-responder/build.gradle").exists()); + assertTrue(new File("build/unitTest/infrastructure/entry-points/rsocket-responder/src/main/java/co/com/bancolombia/controller/RsocketController.java").exists()); + assertTrue(new File("build/unitTest/infrastructure/entry-points/rsocket-responder/src/test/java/co/com/bancolombia/controller").exists()); + } + @Test public void generateEntryPointApiRestWithDefaultServer() throws IOException, CleanException { // Arrange @@ -173,6 +191,7 @@ public void generateEntryPointApiRestWithTomcatServer() throws IOException, Clea @Test public void generateEntryPointReactiveWebWithoutRouterFunctions() throws IOException, CleanException { // Arrange + setup(GenerateStructureTask.ProjectType.REACTIVE); task.setType(ModuleFactoryEntryPoint.EntryPointType.WEBFLUX); task.setRouter(Constants.BooleanOption.FALSE); // Act @@ -187,6 +206,7 @@ public void generateEntryPointReactiveWebWithoutRouterFunctions() throws IOExcep @Test public void generateEntryPointReactiveWebWithRouterFunctions() throws IOException, CleanException { // Arrange + setup(GenerateStructureTask.ProjectType.REACTIVE); task.setType(ModuleFactoryEntryPoint.EntryPointType.WEBFLUX); task.setRouter(Constants.BooleanOption.TRUE); @@ -201,6 +221,7 @@ public void generateEntryPointReactiveWebWithRouterFunctions() throws IOExceptio @Test public void generateEntryPointReactiveWebWithDefaultOptionFunctions() throws IOException, CleanException { // Arrange + setup(GenerateStructureTask.ProjectType.REACTIVE); task.setType(ModuleFactoryEntryPoint.EntryPointType.WEBFLUX); // Act