FINERACT-1532: Create an example for pluggability

This commit is contained in:
Aleksandar Vidakovic
2022-03-25 08:56:59 +01:00
committed by Aleksandar Vidakovic
parent 2ebde6d080
commit 83eb0842dc
60 changed files with 1354 additions and 105 deletions
+14 -4
View File
@@ -61,7 +61,7 @@ plugins {
id 'com.github.jk1.dependency-license-report' version '2.1' apply false id 'com.github.jk1.dependency-license-report' version '2.1' apply false
id 'org.openapi.generator' version '4.3.1' apply false id 'org.openapi.generator' version '4.3.1' apply false
id 'org.zeroturnaround.gradle.jrebel' version '1.1.11' apply false id 'org.zeroturnaround.gradle.jrebel' version '1.1.11' apply false
id 'org.springframework.boot' version '2.6.4' apply false id 'org.springframework.boot' version '2.6.5' apply false
id 'net.ltgt.errorprone' version '2.0.2' apply false id 'net.ltgt.errorprone' version '2.0.2' apply false
id 'io.swagger.core.v3.swagger-gradle-plugin' version '2.1.13' apply false id 'io.swagger.core.v3.swagger-gradle-plugin' version '2.1.13' apply false
id 'com.gorylenko.gradle-git-properties' version '2.4.0' apply false id 'com.gorylenko.gradle-git-properties' version '2.4.0' apply false
@@ -99,6 +99,8 @@ allprojects {
mavenCentral() mavenCentral()
} }
apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management' apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.adarshr.test-logger' apply plugin: 'com.adarshr.test-logger'
apply plugin: 'com.diffplug.spotless' apply plugin: 'com.diffplug.spotless'
@@ -112,10 +114,11 @@ allprojects {
dependencyManagement { dependencyManagement {
imports { imports {
mavenBom 'org.springframework:spring-framework-bom:5.3.17' mavenBom 'org.springframework:spring-framework-bom:5.3.17'
mavenBom 'org.springframework.boot:spring-boot-dependencies:2.6.4' mavenBom 'org.springframework.boot:spring-boot-dependencies:2.6.5'
mavenBom 'org.junit:junit-bom:5.8.2' mavenBom 'org.junit:junit-bom:5.8.2'
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.2' mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.2'
mavenBom 'io.cucumber:cucumber-bom:7.2.3' mavenBom 'io.cucumber:cucumber-bom:7.2.3'
mavenBom 'io.netty:netty-bom:4.1.75.Final'
} }
dependencies { dependencies {
@@ -139,13 +142,13 @@ allprojects {
dependency 'com.jayway.jsonpath:json-path:2.7.0' dependency 'com.jayway.jsonpath:json-path:2.7.0'
dependency 'org.apache.tika:tika-core:2.3.0' dependency 'org.apache.tika:tika-core:2.3.0'
dependency 'org.apache.httpcomponents:httpclient:4.5.13' dependency 'org.apache.httpcomponents:httpclient:4.5.13'
dependency 'io.swagger.core.v3:swagger-annotations:2.1.13'
dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4' dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4'
dependency 'jakarta.jms:jakarta.jms-api:2.0.3' dependency 'jakarta.jms:jakarta.jms-api:2.0.3'
dependency 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' // Swagger needs exactly this version dependency 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' // Swagger needs exactly this version
dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version
dependency 'org.apache.activemq:activemq-broker:5.16.4' dependency 'org.apache.activemq:activemq-broker:5.16.4'
dependency 'org.apache.bval:org.apache.bval.bundle:2.0.5' dependency 'org.apache.bval:org.apache.bval.bundle:2.0.5'
dependency 'joda-time:joda-time:2.10.14'
dependency 'io.github.classgraph:classgraph:4.8.141' dependency 'io.github.classgraph:classgraph:4.8.141'
dependency 'org.awaitility:awaitility:4.2.0' dependency 'org.awaitility:awaitility:4.2.0'
@@ -191,6 +194,12 @@ allprojects {
dependency "org.bouncycastle:bcpkix-jdk15to18:1.70" dependency "org.bouncycastle:bcpkix-jdk15to18:1.70"
dependency "org.bouncycastle:bcprov-jdk15to18:1.70" dependency "org.bouncycastle:bcprov-jdk15to18:1.70"
dependency 'io.swagger.core.v3:swagger-annotations:2.1.13'
dependency ('io.swagger.core.v3:swagger-jaxrs2:2.1.13') {
exclude 'jakarta.activation:jakarta.activation-api'
}
dependency ('org.liquibase:liquibase-core:4.9.0') { dependency ('org.liquibase:liquibase-core:4.9.0') {
exclude 'javax.xml.bind:jaxb-api' exclude 'javax.xml.bind:jaxb-api'
} }
@@ -299,6 +308,7 @@ allprojects {
'**/*.puml', '**/*.puml',
'**/*.github/**', '**/*.github/**',
'**/MANIFEST.MF', '**/MANIFEST.MF',
'**/spring.factories',
'**/*.json', '**/*.json',
'**/*.json.template', '**/*.json.template',
'**/*.txt', '**/*.txt',
@@ -641,7 +651,7 @@ configure(project.fineractJavaProjects) {
cucumber { cucumber {
tags = 'not @ignore' tags = 'not @ignore'
main = 'io.cucumber.core.cli.Main' main = 'io.cucumber.core.cli.Main'
shorten = "argfile" shorten = 'argfile'
} }
// Configuration for spotbugs plugin // Configuration for spotbugs plugin
+20
View File
@@ -1,4 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<FindBugsFilter <FindBugsFilter
xmlns="https://github.com/spotbugs/filter/3.0.0" xmlns="https://github.com/spotbugs/filter/3.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+19
View File
@@ -0,0 +1,19 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'ACME Corp.: Fineract Foo'
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'ACME Corp.: Fineract Foo Service'
group = 'com.acmecorp.fineract.foo.service'
apply from: 'dependencies.gradle'
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
implementation(project(':module:dummy:core'),
'org.springframework:spring-context')
}
@@ -0,0 +1,31 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.acmecorp.fineract.foo.service;
import org.apache.fineract.dummy.core.data.DummyMessage;
import org.apache.fineract.dummy.core.service.DummyService;
import org.springframework.stereotype.Service;
@Service
public class FooDummyServiceImpl implements DummyService {
@Override
public DummyMessage getMessage() {
return new DummyMessage("Hello: FOO!");
}
}
+3
View File
@@ -22,6 +22,9 @@ apply plugin: 'org.openapi.generator'
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
// enable when all tests are migrated
tasks.cucumber.onlyIf {false}
// TODO: @vidakovic we should publish this lib to Maven Central; do in separate PR // TODO: @vidakovic we should publish this lib to Maven Central; do in separate PR
openApiMeta { openApiMeta {
-4
View File
@@ -37,10 +37,6 @@ dependencies {
testImplementation( testImplementation(
'org.junit.jupiter:junit-jupiter-api', 'org.junit.jupiter:junit-jupiter-api',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'com.google.truth:truth', 'com.google.truth:truth',
'com.google.truth.extensions:truth-java8-extension' 'com.google.truth.extensions:truth-java8-extension'
) )
@@ -18,3 +18,4 @@
# #
cucumber.publish.quiet=true cucumber.publish.quiet=true
cucumber.glue=org.apache.fineract
+2 -1
View File
@@ -44,11 +44,12 @@ asciidoctorj {
version = '2.4.0' version = '2.4.0'
attributes = [ attributes = [
version: "$project.version",
generated: "${buildDir}/generated/asciidoc", generated: "${buildDir}/generated/asciidoc",
imagesdir: 'images', imagesdir: 'images',
diagrams: 'diagrams', diagrams: 'diagrams',
years: '2015-2022', years: '2015-2022',
revnumber: "$version".toString(), revnumber: "$project.version".toString(),
rootdir: "$rootDir".toString(), rootdir: "$rootDir".toString(),
baseurl: 'fineract.apache.org' baseurl: 'fineract.apache.org'
] ]
+1
View File
@@ -32,6 +32,7 @@
| Aleksandar Vidakovic | Initial version | 2020-10-26 | 1.4.0 | Aleksandar Vidakovic | Initial version | 2020-10-26 | 1.4.0
| Arnold Galovics | Database architecture section | 2022-03-11 | 1.7.0 | Arnold Galovics | Database architecture section | 2022-03-11 | 1.7.0
| Aleksandar Vidakovic | Module section | 2022-03-23 | 1.7.0
|=== |===
toc::[] toc::[]
+1 -1
View File
@@ -37,4 +37,4 @@ The Fineract API client supports an insecure mode (`FineractClient.Builder#insec
The https://www.fineract.dev demo server runs on Google Cloud. The https://www.fineract.dev demo server runs on Google Cloud.
https://docs.google.com/presentation/d/1-VP4bNkc5kZ3B0yme_vYLiY1qpswnfz8ainnX5fp3l8/[The Running Fineract.dev, SRE style presentation] given at _ApacheCon 20202_ has some related background. https://docs.google.com/presentation/d/1-VP4bNkc5kZ3B0yme_vYLiY1qpswnfz8ainnX5fp3l8/[The Running Fineract.dev, SRE style presentation] given at _ApacheCon 2020_ has some related background.
@@ -0,0 +1,17 @@
@startsalt
{
{T
+ custom
++ mycustom
+++ service
++++ src
+++++ main
++++++ java
+++++++ org.apache.fineract.mymodule.service
++++++++ MyServiceImpl.java
++++ build.gradle
++++ dependencies.gradle
+++ build.gradle
}
}
@endsalt
@@ -0,0 +1,39 @@
@startsalt
{
{T
+ module
++ mymodule
+++ core
++++ src
+++++ main
++++++ java
+++++++ org.apache.fineract.mymodule.core
++++++++ data
+++++++++ MyData.java
++++++++ service
+++++++++ MyService.java
++++ build.gradle
++++ dependencies.gradle
+++ service
++++ src
+++++ main
++++++ java
+++++++ org.apache.fineract.mymodule.service
++++++++ MyServiceImpl.java
++++ build.gradle
++++ dependencies.gradle
+++ starter
++++ src
+++++ main
++++++ java
+++++++ org.apache.fineract.mymodule.starter
++++++++ MyAutoConfiguration.java
++++ resources
+++++ META-INF
++++++ spring.factories
++++ build.gradle
++++ dependencies.gradle
+++ build.gradle
}
}
@endsalt
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+4
View File
@@ -4,6 +4,8 @@ include::config.adoc[]
= Fineract Documentation = Fineract Documentation
include::license.adoc[]
include::preface.adoc[] include::preface.adoc[]
include::colophon.adoc[] include::colophon.adoc[]
@@ -14,6 +16,8 @@ include::deployment.adoc[]
include::architecture.adoc[] include::architecture.adoc[]
include::modules.adoc[]
include::oauth.adoc[] include::oauth.adoc[]
// include::api.adoc[] // include::api.adoc[]
+15
View File
@@ -0,0 +1,15 @@
== License
Copyright 2022 Apache Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+250
View File
@@ -0,0 +1,250 @@
== Modules
Currently modules are a proof of concept feature in Fineract.
=== How to create a Fineract module
NOTE: At the moment the only module we have that follows these stricter guidelines is the example module we describe in detail in the next section. In the future we will try to split up Fineract's monolithic code base and move features (like loan product, accounts etc.) to separate modules as described here. Before that happens we need to do some code cleanups first.
1. Create a folder under `module` and name it _mymodule_.
1. Create a folder named _core_ under `module/mymodule`.
1. Create a folder named _service_ under `module/mymodule`.
1. Create a folder named _starter_ under `module/mymodule`.
1. Setup folders and Gradle build files for a Java project under `core`, `service` and `starter`.
1. Create a text file called _spring.factories in folder `modules/mymodule/starter/src/main/resources/META-INF`.
+
The final folder and file structure should look somewhat like this:
+
[plantuml, format=svg]
----
include::diagrams/modules-folder-structure.puml[]
----
1. Make sure that your new modules are present in settings.gradle:
+
[source,groovy]
----
rootProject.name='fineract'
include ':module:mymodule:core' // <.>
include ':module:mymodule:service'
include ':module:mymodule:starter'
include ':fineract-provider'
include ':fineract-war'
include ':integration-tests'
include ':twofactor-tests'
include ':oauth2-tests'
include ':fineract-client'
include ':fineract-doc'
----
<.> The `settings.gradle` file should contain something like this (and following).
=== How to replace an existing Fineract service
Creating customizations for Fineract services is easy. The method described here will work both with our future module guidelines (aka "clean room" modules) and with the intermediary solution we will put in place to avoid major refactorings.
You can of course choose whatever folder/project structure you like for your custom modules. But we'll describe here some best practices to avoid merge conflicts when fetching updates from Fineract's upstream Git repository. For the time being we suggest to create your custom modules in the same folder as Fineract in a forked Git repo.
As soon as we can publish Fineract module JARs to Maven Central you'll have more freedom to setup your projects (including to setup separate Git repos).
1. Create a folder under `custom` and name it _mycustom_.
1. Create a folder named _service_ under `custom/mycustom`.
1. Setup folders and Gradle build files for a Java project under `service`.
1. Create a text file called _spring.factories in folder `modules/mymodule/starter/src/main/resources/META-INF`:
+
[source,properties]
----
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.fineract.mymodule.starter.MyAutoConfiguration
----
+
The final folder and file structure should look somewhat like this:
+
[plantuml, format=svg]
----
include::diagrams/custom-folder-structure.puml[]
----
+
Please make sure that your `service` module's build.gradle file has a unique group:
+
[source,groovy]
----
description = 'My Company: Fineract My Custom Service'
group = 'com.mycompany.fineract.custom.service' // <.>
apply from: 'dependencies.gradle'
----
+
<.> The best choice here is to name it like you would name your JAR artifact on Maven Central.
1. Make sure that your custom modules are present in settings.gradle: :
+
[source,groovy]
----
rootProject.name='fineract'
include ':custom:mycustom:service' // <.>
include ':fineract-provider'
include ':fineract-war'
include ':integration-tests'
include ':twofactor-tests'
include ':oauth2-tests'
include ':fineract-client'
include ':fineract-doc'
----
<.> The `settings.gradle` file should contain something like this.
1. The dependency.gradle file could look something like this:
[source,groovy]
----
dependencies {
implementation(project(':module:mymodule:core'), // <.>
'org.springframework:spring-context')
compileOnly project(':fineract-provider') // <.>
}
----
<.> If you are replacing a "clean room" module then you just need to include a dependency on the modules `core` library.
<.> If you are replacing a service that is not yet extracted from `fineract-provider` then you need to add a dependency on it (only needed during compilation).
1. When the custom module is built then you can add the JAR in your Fineract's libs folder (or in Tomcat's libs folder if you are deploying as a WAR app).
IMPORTANT: Do not include your custom module in `fineract-provider`'s dependency.gradle file. This creates a circular dependency and will fail your build. Instead you have to add your JAR file e. g. to the Docker image (in Fineract's libs folder; similar like we do it with Pentaho reporting). This setup will give you the best developer experience for now with proper source references until we have separate JAR files ready on Maven Central. In the next section we describe the "clean room" (read: where we want to go) type of modules and their customization/replacement; so don't be confused if you see the dependency appearing in `fineract-provider`'s dependency.gradle, in the future you'll be able to do that.
=== Example
==== Dummy Module Structure
We've created a demonstration how modules are supposed to be used. A module has usually at least two sub-modules:
1. a _core_ module that contains mostly Java interfaces of the services and/or other components that we'd like to make replacable. The _core_ module could also contain data (DTO) or domain (entity) classes if necessary.
2. a _service_ module
IMPORTANT: Replacing parts of the existing REST API and extending it with custom endpoints is at the moment out of scope. Probably we'll need to cleanup and improve the REST layer quite a bit (remove boilerplate code as much as possible, use Jackson for JSON de-/serialization instead of the manual GSON mappers/helper everywhere) before this can happen. For now we'll concentrate primarily on service classes.
Let's assume we have a service `DummyService` in our system. The service is outlined as a Java interface in module folder `module/dummy/core`.
[source,java]
----
include::{rootdir}/module/dummy/core/src/main/java/org/apache/fineract/dummy/core/service/DummyService.java[lines=19..]
----
To make it a little bit more interesting the service's only function returns a simple data object that has one string attribute:
[source,java]
----
include::{rootdir}/module/dummy/core/src/main/java/org/apache/fineract/dummy/core/data/DummyMessage.java[lines=19..]
----
A default implementation of `DummyService` is provided with `DummyServiceImpl`:
[source,java]
----
include::{rootdir}/module/dummy/service/src/main/java/org/apache/fineract/dummy/service/DummyServiceImpl.java[lines=19..]
----
As you can see there are no annotations like `@Service` or `@Component`. To have full control over the instantiation and dependency injection we provide a so called _starter_ module. This module just contains one or more (auto-) configuration classes. In this case it's just one configuration class. This is basically a very simple Spring Java configuration class annotated with `@Configuration` containing one method that instantiates our default service implementation (annotated with `@Bean`):
[source,java]
----
include::{rootdir}/module/dummy/starter/src/main/java/org/apache/fineract/dummy/starter/DummyAutoConfiguration.java[lines=19..]
----
The interesting part is this line:
[source,java]
----
include::{rootdir}/module/dummy/starter/src/main/java/org/apache/fineract/dummy/starter/DummyAutoConfiguration.java[lines=28..28]
----
This annotation ensures that our default implementation is only instantiated if no other implementation for `DummyService` is provided. There's only one piece missing to make auto configuration work seamlessly and without any explicit configuration in `fineract-provider`:
[source,properties]
----
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.fineract.dummy.starter.DummyAutoConfiguration
----
This means if we would only include the following dependencies in our main project (e. g. `fineract-provider`)...
[source,groovy]
----
dependencies {
implementation project(':module:dummy:core')
implementation project(':module:dummy:service')
implementation project(':module:dummy:starter')
}
----
\... then the message object would contain
[source,text]
----
Hello: DEFAULT DUMMY!
----
==== Replace the default implementation of DummyService
This is why we created module `foo` in folder `module/foo`. There's only only one sub-module that contains an implementation of `DummyService` that will replace the default implementation:
[source,java]
----
include::{rootdir}/custom/foo/service/src/main/java/com/acmecorp/fineract/foo/service/FooDummyServiceImpl.java[lines=19..]
----
As you can see we have no other sub-modules (e. g. there's no starter module) and this implementation contains the well known `@Service` annotation. If we add this dependency to our main project...
[source,groovy]
----
dependencies {
implementation project(':module:dummy:core')
implementation project(':module:dummy:service')
implementation project(':module:dummy:starter')
implementation project(':custom:foo:service')
}
----
\... then the expected behavior is that we'll get the message
[source,text]
----
Hello: Foo!
----
==== Testing
There's a simple unit test in `fineract-provider` that demonstrates both scenarios (without any override of the default implementation and with an overriding alternative implementation)
[source,gherkin]
----
include::{rootdir}/fineract-provider/src/test/resources/features/module/modules.example.feature[]
----
Here's the Spring Java configuration that does *NOT* load the `foo` module:
[source,java]
----
include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/module/example/TestDefaultConfiguration.java[lines=19..]
----
\... and here's the configuration that loads the `foo` module (and overrides the deefault implementation)
[source,java]
----
include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/module/example/TestFooConfiguration.java[lines=19..]
----
The important part in the `foo` configuration is this line:
[source,java]
----
include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/module/example/TestFooConfiguration.java[lines=26..26]
----
==== Deployment
Modules (better: the JAR files) only need to be dropped in Fineract's `libs` folder. Dynamic loading of external JARs is provided since Fineract version 1.5.0.
==== How can I start replacing services now
As said the "clean room" modules will take a while to arrive. In the meanwhile we'll prepare the existing monolithic code base for pluggability. As a proof of concept the services `org.apache.fineract.portfolio.note.service.NoteReadPlatformService` and `org.apache.fineract.portfolio.note.service.NoteWritePlatformService` can be replaced/overriden by custom implementations. We'll add more shortly and will list them here.
==== Outlook
If this proof of concept is accepted we could refactor Fineract's services one by one and rearrange them in the proposed module structure. As discussed at ApacheCon 2021 there will be most likely some work to do to properly extract those modules (loan product, client, savings account etc.) from the monolithic code base. The main challenge are cross-dependencies between the _modules_, but there are major benefits if we split up the code like this. One - as demonstrated - is the replacement of existing default functionality. But with modules we'll also be able to finally publish Fineract JAR files to Maven Central. Those JAR files can be used to write extensions and customizations *WITHOUT* forking the whole Fineract Github repository. With modules users will also be able to create custom Fineract distributions, e. g. slimmed down to contain only the bare minimum needed in production.
+5 -1
View File
@@ -135,7 +135,11 @@ dependencies {
'io.cucumber:cucumber-java8', 'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine', 'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring', 'io.cucumber:cucumber-spring',
'io.github.classgraph:classgraph' 'io.github.classgraph:classgraph',
project(':module:dummy:core'),
project(':module:dummy:service'),
project(':module:dummy:starter'),
project(':custom:foo:service'),
) )
testImplementation ('org.springframework.boot:spring-boot-starter-test') { testImplementation ('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'com.jayway.jsonpath', module: 'json-path' exclude group: 'com.jayway.jsonpath', module: 'json-path'
@@ -1,63 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.boot;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
public abstract class ApplicationExitUtil {
private static final Logger LOG = LoggerFactory.getLogger(ApplicationExitUtil.class);
private ApplicationExitUtil() {}
public static void waitForKeyPressToCleanlyExit(ConfigurableApplicationContext ctx) throws IOException {
// In some environments, System.console() is not available (e.g. "./gradlew bootRun", or in a container, or in
// Eclipse, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=122429.
//
// Also (but as a separate problem), e.g. in Eclipse, the Shutdown Hooks are not invoked on Exit with the Red
// Button of the Console view (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38016); that's a problem e.g.
// in the case of MariaDB4j, because then mysqld won't be stopped; same problem similarly applies on any other
// library else that relies on an orderly Spring Context / JVM shutdown.
//
if (System.console() == null) {
LOG.info("\nNo Console available, running until stopped by signal/Ctrl-C...");
boolean interrupted = false;
do {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
interrupted = true;
}
} while (!interrupted);
} else {
LOG.info("\nHit Enter to quit...");
BufferedReader d = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
d.readLine();
ctx.stop();
ctx.close();
}
}
}
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.infrastructure.core.boot; package org.apache.fineract.infrastructure.core.config;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -133,14 +133,13 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatfor
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService; import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.apache.fineract.portfolio.note.data.NoteData; import org.apache.fineract.portfolio.note.data.NoteData;
import org.apache.fineract.portfolio.note.domain.NoteType; import org.apache.fineract.portfolio.note.domain.NoteType;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl; import org.apache.fineract.portfolio.note.service.NoteReadPlatformService;
import org.apache.fineract.portfolio.rate.data.RateData; import org.apache.fineract.portfolio.rate.data.RateData;
import org.apache.fineract.portfolio.rate.service.RateReadService; import org.apache.fineract.portfolio.rate.service.RateReadService;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType; import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -244,7 +243,7 @@ public class LoansApiResource {
private final FromJsonHelper fromJsonHelper; private final FromJsonHelper fromJsonHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final CalendarReadPlatformService calendarReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService;
private final NoteReadPlatformServiceImpl noteReadPlatformService; private final NoteReadPlatformService noteReadPlatformService;
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService; private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService; private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
@@ -258,7 +257,6 @@ public class LoansApiResource {
private final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService; private final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService;
private final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService; private final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService;
@Autowired
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService, public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
final LoanProductReadPlatformService loanProductReadPlatformService, final LoanProductReadPlatformService loanProductReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService, final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
@@ -272,7 +270,7 @@ public class LoansApiResource {
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer, final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper, final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformService noteReadPlatformService,
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl, final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService, final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
@@ -32,18 +32,14 @@ import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.portfolio.note.data.NoteData; import org.apache.fineract.portfolio.note.data.NoteData;
import org.apache.fineract.portfolio.note.domain.NoteType; import org.apache.fineract.portfolio.note.domain.NoteType;
import org.apache.fineract.portfolio.note.exception.NoteNotFoundException; import org.apache.fineract.portfolio.note.exception.NoteNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class NoteReadPlatformServiceImpl implements NoteReadPlatformService { public class NoteReadPlatformServiceImpl implements NoteReadPlatformService {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
@Autowired
public NoteReadPlatformServiceImpl(final RoutingDataSource dataSource) { public NoteReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
@@ -42,10 +42,7 @@ import org.apache.fineract.portfolio.note.exception.NoteResourceNotSupportedExce
import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer; import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatformService { public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(NoteWritePlatformServiceJpaRepositoryImpl.class); private static final Logger LOG = LoggerFactory.getLogger(NoteWritePlatformServiceJpaRepositoryImpl.class);
@@ -56,7 +53,6 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
private final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Autowired
public NoteWritePlatformServiceJpaRepositoryImpl(final NoteRepository noteRepository, final ClientRepositoryWrapper clientRepository, public NoteWritePlatformServiceJpaRepositoryImpl(final NoteRepository noteRepository, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final LoanRepositoryWrapper loanRepository, final GroupRepository groupRepository, final LoanRepositoryWrapper loanRepository,
final LoanTransactionRepository loanTransactionRepository, final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) { final LoanTransactionRepository loanTransactionRepository, final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
@@ -0,0 +1,53 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.note.starter;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.group.domain.GroupRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformService;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformServiceJpaRepositoryImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NoteAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NoteReadPlatformService noteReadPlatformService(RoutingDataSource routingDataSource) {
return new NoteReadPlatformServiceImpl(routingDataSource);
}
@Bean
@ConditionalOnMissingBean
public NoteWritePlatformService noteWritePlatformService(NoteRepository noteRepository, ClientRepositoryWrapper clientRepository,
GroupRepository groupRepository, LoanRepositoryWrapper loanRepository, LoanTransactionRepository loanTransactionRepository,
NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
return new NoteWritePlatformServiceJpaRepositoryImpl(noteRepository, clientRepository, groupRepository, loanRepository,
loanTransactionRepository, fromApiJsonDeserializer);
}
}
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.mock;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration;
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService; import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService;
@@ -37,19 +36,37 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness; import org.mockito.quality.Strictness;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/** /**
* Spring @Configuration which does not require a running database. It also does not load any job configuration (as they * Spring @Configuration which does not require a running database. It also does not load any job configuration (as they
* are in the DB), thus nor starts any background jobs. For some integration tests, this may be perfectly sufficient * are in the DB), thus nor starts any background jobs. For some integration tests, this may be perfectly sufficient
* (and faster to run such tests). * (and faster to run such tests).
*/ */
@EnableConfigurationProperties({ FineractProperties.class }) @Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, GsonAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
LiquibaseAutoConfiguration.class })
@EnableTransactionManagement
@EnableWebSecurity
@EnableConfigurationProperties({ FineractProperties.class, LiquibaseProperties.class })
@ComponentScan(basePackages = "org.apache.fineract")
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) @MockitoSettings(strictness = Strictness.LENIENT)
public class TestConfiguration extends AbstractApplicationConfiguration { public class TestConfiguration {
@Bean @Bean
public TenantDataSourceFactory tenantDataSourceFactory() { public TenantDataSourceFactory tenantDataSourceFactory() {
@@ -0,0 +1,54 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.example;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java8.En;
import org.apache.fineract.dummy.core.data.DummyMessage;
import org.apache.fineract.dummy.core.service.DummyService;
import org.apache.fineract.dummy.starter.DummyAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
public class DummyServiceStepDefinitions implements En {
private DummyMessage message;
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DummyAutoConfiguration.class));
public DummyServiceStepDefinitions() {
Given("/^A dummy service configuration (.*)$/", (String configurationClass) -> {
contextRunner = contextRunner.withUserConfiguration(Class.forName(configurationClass.trim()));
});
When("The user gets the dummy service message", () -> {
contextRunner.run((ctx) -> {
assertThat(ctx).hasSingleBean(DummyService.class);
this.message = ctx.getBean(DummyService.class).getMessage();
});
});
Then("/^The dummy service message should match (.*)$/", (String msg) -> {
assertThat(this.message).isNotNull();
assertThat(msg).isEqualTo(this.message.getMessage());
});
}
}
@@ -0,0 +1,25 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.example;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@EnableConfigurationProperties({ FineractProperties.class })
public class TestDefaultConfiguration {}
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.example;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
@EnableConfigurationProperties({ FineractProperties.class })
@ComponentScan("com.acmecorp.fineract.foo")
public class TestFooConfiguration {}
@@ -0,0 +1,58 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.service;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java8.En;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
public class ServiceStepDefinitions implements En {
private Class interfaceClass;
private Class implementationClass;
private ApplicationContextRunner contextRunner;
public ServiceStepDefinitions() {
Given("/^An auto configuration (.*) and a service configuration (.*)$/",
(String autoConfigurationClassName, String configurationClassName) -> {
contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(Class.forName(autoConfigurationClassName)))
.withUserConfiguration(Class.forName(configurationClassName.trim()));
});
When("/^The user retrieves the service of interface class (.*)$/", (String interfaceClassName) -> {
contextRunner.run((ctx) -> {
this.interfaceClass = Class.forName(interfaceClassName.trim());
assertThat(this.interfaceClass.isInterface()).isTrue();
assertThat(ctx).hasSingleBean(this.interfaceClass);
this.implementationClass = ctx.getBean(interfaceClass).getClass();
});
});
Then("/^The service class should match (.*)$/", (String serviceClassName) -> {
assertThat(Class.forName(serviceClassName.trim())).isEqualTo(this.implementationClass);
});
}
}
@@ -0,0 +1,84 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.service;
import static org.mockito.Mockito.mock;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.group.domain.GroupRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({ FineractProperties.class })
public class TestDefaultConfiguration {
// NOTE: unfortunately an abastract base class that contains all these mock functions won't work
@Bean
public FromJsonHelper fromJsonHelper() {
return mock(FromJsonHelper.class);
}
@Bean
public RoutingDataSourceServiceFactory routingDataSourceServiceFactory() {
return mock(RoutingDataSourceServiceFactory.class);
}
@Bean
public RoutingDataSource routingDataSource() {
return mock(RoutingDataSource.class);
}
@Bean
public NoteRepository noteRepository() {
return mock(NoteRepository.class);
}
@Bean
public ClientRepositoryWrapper clientRepository() {
return mock(ClientRepositoryWrapper.class);
}
@Bean
public GroupRepository groupRepository() {
return mock(GroupRepository.class);
}
@Bean
public LoanRepositoryWrapper loanRepository() {
return mock(LoanRepositoryWrapper.class);
}
@Bean
public LoanTransactionRepository loanTransactionRepository() {
return mock(LoanTransactionRepository.class);
}
@Bean
public NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer(FromJsonHelper fromJsonHelper) {
return new NoteCommandFromApiJsonDeserializer(fromJsonHelper);
}
}
@@ -0,0 +1,83 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.service;
import static org.mockito.Mockito.mock;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.group.domain.GroupRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("org.apache.fineract.module.service.custom")
public class TestOverrideConfiguration {
// NOTE: unfortunately an abastract base class that contains all these mock functions won't work
@Bean
public FromJsonHelper fromJsonHelper() {
return mock(FromJsonHelper.class);
}
@Bean
public RoutingDataSourceServiceFactory routingDataSourceServiceFactory() {
return mock(RoutingDataSourceServiceFactory.class);
}
@Bean
public RoutingDataSource routingDataSource() {
return mock(RoutingDataSource.class);
}
@Bean
public NoteRepository noteRepository() {
return mock(NoteRepository.class);
}
@Bean
public ClientRepositoryWrapper clientRepository() {
return mock(ClientRepositoryWrapper.class);
}
@Bean
public GroupRepository groupRepository() {
return mock(GroupRepository.class);
}
@Bean
public LoanRepositoryWrapper loanRepository() {
return mock(LoanRepositoryWrapper.class);
}
@Bean
public LoanTransactionRepository loanTransactionRepository() {
return mock(LoanTransactionRepository.class);
}
@Bean
public NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer(FromJsonHelper fromJsonHelper) {
return new NoteCommandFromApiJsonDeserializer(fromJsonHelper);
}
}
@@ -0,0 +1,38 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.service.custom;
import java.util.Collection;
import org.apache.fineract.portfolio.note.data.NoteData;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformService;
import org.springframework.stereotype.Service;
@Service
public class CustomNoteReadPlatformService implements NoteReadPlatformService {
@Override
public NoteData retrieveNote(Long noteId, Long resourceId, Integer noteTypeId) {
return null;
}
@Override
public Collection<NoteData> retrieveNotesByResource(Long resourceId, Integer noteTypeId) {
return null;
}
}
@@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.module.service.custom;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
import org.springframework.stereotype.Service;
@Service
public class CustomNoteWritePlatformService implements NoteWritePlatformService {
@Override
public CommandProcessingResult createNote(JsonCommand command) {
return null;
}
@Override
public CommandProcessingResult updateNote(JsonCommand command) {
return null;
}
@Override
public CommandProcessingResult deleteNote(JsonCommand command) {
return null;
}
@Override
public void createAndPersistClientNote(Client client, JsonCommand command) {
}
}
@@ -18,3 +18,4 @@
# #
cucumber.publish.quiet=true cucumber.publish.quiet=true
cucumber.glue=org.apache.fineract
@@ -0,0 +1,12 @@
Feature: Example Modules
@modules
Scenario Outline: Verify that the dummy service returns the correct message
Given A dummy service configuration <configurationClass>
When The user gets the dummy service message
Then The dummy service message should match <message>
Examples:
| configurationClass | message |
| org.apache.fineract.module.example.TestDefaultConfiguration | Hello: DEFAULT DUMMY! |
| org.apache.fineract.module.example.TestFooConfiguration | Hello: FOO! |
@@ -0,0 +1,14 @@
Feature: Service Modules
@modules
Scenario Outline: Verify that the dummy service returns the correct message
Given An auto configuration <autoConfigurationClass> and a service configuration <configurationClass>
When The user retrieves the service of interface class <interfaceClass>
Then The service class should match <serviceClass>
Examples:
| autoConfigurationClass | configurationClass | interfaceClass | serviceClass |
| org.apache.fineract.portfolio.note.starter.NoteAutoConfiguration | org.apache.fineract.module.service.TestDefaultConfiguration | org.apache.fineract.portfolio.note.service.NoteReadPlatformService | org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl |
| org.apache.fineract.portfolio.note.starter.NoteAutoConfiguration | org.apache.fineract.module.service.TestDefaultConfiguration | org.apache.fineract.portfolio.note.service.NoteWritePlatformService | org.apache.fineract.portfolio.note.service.NoteWritePlatformServiceJpaRepositoryImpl |
| org.apache.fineract.portfolio.note.starter.NoteAutoConfiguration | org.apache.fineract.module.service.TestOverrideConfiguration | org.apache.fineract.portfolio.note.service.NoteReadPlatformService | org.apache.fineract.module.service.custom.CustomNoteReadPlatformService |
| org.apache.fineract.portfolio.note.starter.NoteAutoConfiguration | org.apache.fineract.module.service.TestOverrideConfiguration | org.apache.fineract.portfolio.note.service.NoteWritePlatformService | org.apache.fineract.module.service.custom.CustomNoteWritePlatformService |
+1 -1
View File
@@ -16,6 +16,6 @@
# specific language governing permissions and limitations # specific language governing permissions and limitations
# under the License. # under the License.
# #
org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
buildType=BUILD buildType=BUILD
org.gradle.caching=true org.gradle.caching=true
+4
View File
@@ -28,6 +28,9 @@ configurations {
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
// enable when all tests are migrated
tasks.cucumber.onlyIf {false}
cargo { cargo {
containerId "tomcat9x" containerId "tomcat9x"
@@ -39,6 +42,7 @@ cargo {
local { local {
logLevel = 'medium' logLevel = 'medium'
// outputFile = file('build/output.log')
installer { installer {
installConfiguration = configurations.tomcat installConfiguration = configurations.tomcat
downloadDir = file("$buildDir/download") downloadDir = file("$buildDir/download")
@@ -18,3 +18,4 @@
# #
cucumber.publish.quiet=true cucumber.publish.quiet=true
cucumber.glue=org.apache.fineract
+19
View File
@@ -0,0 +1,19 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Module'
+19
View File
@@ -0,0 +1,19 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Module: Dummy'
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Module: Dummy Core'
group = 'org.apache.fineract.dummy.core'
apply from: 'dependencies.gradle'
+22
View File
@@ -0,0 +1,22 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
implementation('org.springframework:spring-context')
}
@@ -0,0 +1,35 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.dummy.core.data;
public class DummyMessage {
private String message;
public DummyMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
@@ -0,0 +1,25 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.dummy.core.service;
import org.apache.fineract.dummy.core.data.DummyMessage;
public interface DummyService {
DummyMessage getMessage();
}
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Module: Dummy Service'
group = 'org.apache.fineract.dummy.service'
apply from: 'dependencies.gradle'
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
implementation(project(':module:dummy:core'),
'org.springframework:spring-context')
}
@@ -0,0 +1,29 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.dummy.service;
import org.apache.fineract.dummy.core.data.DummyMessage;
import org.apache.fineract.dummy.core.service.DummyService;
public class DummyServiceImpl implements DummyService {
@Override
public DummyMessage getMessage() {
return new DummyMessage("Hello: DEFAULT DUMMY!");
}
}
+23
View File
@@ -0,0 +1,23 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Module: Dummy Starter'
group = 'org.apache.fineract.dummy.starter'
apply from: 'dependencies.gradle'
+24
View File
@@ -0,0 +1,24 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
implementation(project(':module:dummy:core'),
project(':module:dummy:service'),
'org.springframework.boot:spring-boot-starter')
}
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.dummy.starter;
import org.apache.fineract.dummy.core.service.DummyService;
import org.apache.fineract.dummy.service.DummyServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnMissingBean(DummyService.class)
public class DummyAutoConfiguration {
@Bean
public DummyService dummyService() {
return new DummyServiceImpl();
}
}
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.fineract.dummy.starter.DummyAutoConfiguration
+5
View File
@@ -28,6 +28,9 @@ configurations {
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
// enable when all tests are migrated
tasks.cucumber.onlyIf {false}
cargo { cargo {
containerId "tomcat9x" containerId "tomcat9x"
@@ -38,6 +41,8 @@ cargo {
} }
local { local {
// logLevel = 'medium'
// outputFile = file('build/output.log')
installer { installer {
installConfiguration = configurations.tomcat installConfiguration = configurations.tomcat
downloadDir = file("$buildDir/download") downloadDir = file("$buildDir/download")
-6
View File
@@ -26,12 +26,6 @@ dependencies {
'org.junit.jupiter:junit-jupiter-api', 'org.junit.jupiter:junit-jupiter-api',
'org.bouncycastle:bcpkix-jdk15to18', 'org.bouncycastle:bcpkix-jdk15to18',
'org.bouncycastle:bcprov-jdk15to18', 'org.bouncycastle:bcprov-jdk15to18',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'com.intuit.karate:karate-junit5',
) )
testImplementation ('io.rest-assured:rest-assured') { testImplementation ('io.rest-assured:rest-assured') {
exclude group: 'commons-logging' exclude group: 'commons-logging'
@@ -0,0 +1,21 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
cucumber.publish.quiet=true
cucumber.glue=org.apache.fineract
+4
View File
@@ -17,6 +17,10 @@
* under the License. * under the License.
*/ */
rootProject.name='fineract' rootProject.name='fineract'
include ':module:dummy:core'
include ':module:dummy:service'
include ':module:dummy:starter'
include ':custom:foo:service'
include ':fineract-provider' include ':fineract-provider'
include ':fineract-war' include ':fineract-war'
include ':integration-tests' include ':integration-tests'
+5
View File
@@ -28,6 +28,9 @@ configurations {
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
// enable when all tests are migrated
tasks.cucumber.onlyIf {false}
cargo { cargo {
containerId "tomcat9x" containerId "tomcat9x"
@@ -38,6 +41,8 @@ cargo {
} }
local { local {
// logLevel = 'medium'
// outputFile = file('build/output.log')
installer { installer {
installConfiguration = configurations.tomcat installConfiguration = configurations.tomcat
downloadDir = file("$buildDir/download") downloadDir = file("$buildDir/download")
+1 -7
View File
@@ -24,15 +24,9 @@ dependencies {
testImplementation( files("$rootDir/fineract-provider/build/classes/java/main/"), testImplementation( files("$rootDir/fineract-provider/build/classes/java/main/"),
project(path: ':fineract-provider', configuration: 'runtimeElements'), project(path: ':fineract-provider', configuration: 'runtimeElements'),
'org.junit.jupiter:junit-jupiter-api', 'org.junit.jupiter:junit-jupiter-api',
'com.icegreen:greenmail-junit5',
'org.bouncycastle:bcpkix-jdk15to18', 'org.bouncycastle:bcpkix-jdk15to18',
'org.bouncycastle:bcprov-jdk15to18', 'org.bouncycastle:bcprov-jdk15to18',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'com.icegreen:greenmail-junit5',
'com.intuit.karate:karate-junit5',
) )
testImplementation ('io.rest-assured:rest-assured') { testImplementation ('io.rest-assured:rest-assured') {
exclude group: 'commons-logging' exclude group: 'commons-logging'
@@ -0,0 +1,21 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
cucumber.publish.quiet=true
cucumber.glue=org.apache.fineract