Skip to content

Commit 9d4d848

Browse files
committed
Fixes to support MySQL 8 due to specific reserved keywords
1 parent 6aa668a commit 9d4d848

10 files changed

Lines changed: 137 additions & 54 deletions

File tree

File renamed without changes.

.github/workflows/build-mysql.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Fineract Gradle build - MySQL
2+
on: [push, pull_request]
3+
4+
permissions:
5+
contents: read
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-20.04
10+
11+
services:
12+
mariad:
13+
image: mysql:8.0
14+
ports:
15+
- 3306:3306
16+
env:
17+
MYSQL_ROOT_PASSWORD: mysql
18+
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
19+
20+
mock-oauth2-server:
21+
image: ghcr.io/navikt/mock-oauth2-server:0.4.0
22+
ports:
23+
- 9000:9000
24+
env:
25+
SERVER_PORT: 9000
26+
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
27+
28+
env:
29+
TZ: Asia/Kolkata
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v3
33+
- name: Set up JDK 17
34+
uses: actions/setup-java@v3
35+
with:
36+
java-version: '17'
37+
distribution: 'zulu'
38+
cache: gradle
39+
- uses: actions/setup-node@v3
40+
with:
41+
node-version: 16
42+
- name: Congfigure vega-cli
43+
run: npm i -g vega-cli --unsafe
44+
- name: Validate Gradle wrapper
45+
uses: gradle/wrapper-validation-action@859c33240bd026ce8d5f711f5adcc65c2f8eafc1
46+
- name: Verify MariaDB connection
47+
run: |
48+
while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do
49+
sleep 1
50+
done
51+
- name: Initialise databases
52+
run: |
53+
./gradlew --no-daemon -q createMySQLDB -PdbName=fineract_tenants
54+
./gradlew --no-daemon -q createMySQLDB -PdbName=fineract_default
55+
- name: Install additional software
56+
run: |
57+
sudo apt-get update
58+
sudo apt-get install ghostscript graphviz -y
59+
60+
- name: Basic Auth Build & Test
61+
run: ./gradlew --no-daemon -q --console=plain build test --fail-fast doc -x :twofactor-tests:test -x :oauth2-test:test -PdbType=mysql
62+
63+
- name: 2FA Build & Test
64+
run: ./gradlew --no-daemon -q --console=plain :twofactor-tests:test --fail-fast -PdbType=mysql
65+
66+
- name: OAuth2 Build & Test
67+
run: ./gradlew --no-daemon -q --console=plain :oauth2-tests:test --fail-fast -PdbType=mysql

fineract-provider/build.gradle

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ configurations {
151151
dependencies {
152152
driver 'org.mariadb.jdbc:mariadb-java-client:2.7.5'
153153
driver 'org.postgresql:postgresql:42.3.5'
154+
driver 'mysql:mysql-connector-java:8.0.29'
154155
}
155156

156157
URLClassLoader loader = GroovyObject.class.classLoader
@@ -159,15 +160,15 @@ configurations.driver.each {File file ->
159160
}
160161

161162
task createDB {
162-
description= "Creates the MySQL Database. Needs database name to be passed (like: -PdbName=someDBname)"
163+
description= "Creates the MariaDB Database. Needs database name to be passed (like: -PdbName=someDBname)"
163164
doLast {
164165
def sql = Sql.newInstance( 'jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver' )
165166
sql.execute( 'create database '+"`$dbName`" )
166167
}
167168
}
168169

169170
task dropDB {
170-
description= "Drops the specified MySQL database. The database name has to be passed (like: -PdbName=someDBname)"
171+
description= "Drops the specified MariaDB database. The database name has to be passed (like: -PdbName=someDBname)"
171172
doLast {
172173
def sql = Sql.newInstance( 'jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver' )
173174
sql.execute( 'DROP DATABASE '+"`$dbName`")
@@ -190,6 +191,22 @@ task dropPGDB {
190191
}
191192
}
192193

194+
task createMySQLDB {
195+
description= "Creates the MySQL Database. Needs database name to be passed (like: -PdbName=someDBname)"
196+
doLast {
197+
def sql = Sql.newInstance( 'jdbc:mysql://localhost:3306/', mysqlUser, mysqlPassword, 'com.mysql.cj.jdbc.Driver' )
198+
sql.execute( 'create database '+"`$dbName`" )
199+
}
200+
}
201+
202+
task dropMySQLDB {
203+
description= "Drops the specified MySQL database. The database name has to be passed (like: -PdbName=someDBname)"
204+
doLast {
205+
def sql = Sql.newInstance( 'jdbc:mysql://localhost:3306/', mysqlUser, mysqlPassword, 'com.mysql.cj.jdbc.Driver' )
206+
sql.execute( 'DROP DATABASE '+"`$dbName`")
207+
}
208+
}
209+
193210
task setBlankPassword {
194211
doLast {
195212
def sql = Sql.newInstance( 'jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver' )

fineract-provider/src/main/java/org/apache/fineract/commands/service/AuditReadPlatformServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ public AuditSearchData retrieveSearchTemplate(final String useType) {
425425

426426
sql = " select distinct(entity_name) as entityName from m_permission p ";
427427
sql += makercheckerCapabilityOnly(useType, currentUser);
428-
sql += " order by (CASE WHEN grouping = 'datatable' THEN 'ZZZ' ELSE entity_name END), entity_name";
428+
sql += " order by (CASE WHEN " + sqlGenerator.escape("grouping") + " = 'datatable' THEN 'ZZZ' ELSE entity_name END), entity_name";
429429
final EntityNamesMapper mapper2 = new EntityNamesMapper();
430430
final List<String> entityNames = this.jdbcTemplate.query(sql, mapper2); // NOSONAR
431431

fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseTypeResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public class DatabaseTypeResolver implements InitializingBean {
3030

3131
private final Map<String, DatabaseType> driverMapping = Map.of("org.mariadb.jdbc.Driver", DatabaseType.MYSQL, "com.mysql.jdbc.Driver",
32-
DatabaseType.MYSQL, "org.postgresql.Driver", DatabaseType.POSTGRESQL);
32+
DatabaseType.MYSQL, "com.mysql.cj.jdbc.Driver", DatabaseType.MYSQL, "org.postgresql.Driver", DatabaseType.POSTGRESQL);
3333

3434
private final AtomicReference<DatabaseType> currentDatabaseType = new AtomicReference<>();
3535
private final HikariConfig hikariConfig;

fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import java.util.Locale;
3636
import java.util.Map;
3737
import java.util.Set;
38+
import java.util.stream.Stream;
3839
import javax.persistence.PersistenceException;
40+
import lombok.RequiredArgsConstructor;
3941
import org.apache.commons.lang3.BooleanUtils;
4042
import org.apache.commons.lang3.ObjectUtils;
4143
import org.apache.commons.lang3.StringUtils;
@@ -72,7 +74,6 @@
7274
import org.apache.fineract.useradministration.domain.AppUser;
7375
import org.slf4j.Logger;
7476
import org.slf4j.LoggerFactory;
75-
import org.springframework.beans.factory.annotation.Autowired;
7677
import org.springframework.dao.DataAccessException;
7778
import org.springframework.dao.DataIntegrityViolationException;
7879
import org.springframework.dao.EmptyResultDataAccessException;
@@ -86,6 +87,7 @@
8687
import org.springframework.transaction.annotation.Transactional;
8788

8889
@Service
90+
@RequiredArgsConstructor
8991
public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService {
9092

9193
private static final String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
@@ -110,7 +112,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
110112
private final DatabaseSpecificSQLGenerator sqlGenerator;
111113
private final PlatformSecurityContext context;
112114
private final FromJsonHelper fromJsonHelper;
113-
private final JsonParserHelper helper;
115+
private final JsonParserHelper helper = new JsonParserHelper();
114116
private final GenericDataService genericDataService;
115117
private final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer;
116118
private final ConfigurationDomainService configurationDomainService;
@@ -120,29 +122,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
120122
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
121123
private final SqlInjectionPreventerService preventSqlInjectionService;
122124

123-
@Autowired(required = true)
124-
public ReadWriteNonCoreDataServiceImpl(final JdbcTemplate jdbcTemplate, final NamedParameterJdbcTemplate namedParameterJdbcTemplate,
125-
final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService,
126-
final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CodeReadPlatformService codeReadPlatformService,
127-
final ConfigurationDomainService configurationDomainService, final DataTableValidator dataTableValidator,
128-
final ColumnValidator columnValidator, DatabaseTypeResolver databaseTypeResolver, DatabaseSpecificSQLGenerator sqlGenerator,
129-
SqlInjectionPreventerService sqlInjectionPreventerService) {
130-
this.databaseTypeResolver = databaseTypeResolver;
131-
this.sqlGenerator = sqlGenerator;
132-
this.jdbcTemplate = jdbcTemplate;
133-
this.context = context;
134-
this.fromJsonHelper = fromJsonHelper;
135-
this.helper = new JsonParserHelper();
136-
this.genericDataService = genericDataService;
137-
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
138-
this.codeReadPlatformService = codeReadPlatformService;
139-
this.configurationDomainService = configurationDomainService;
140-
this.dataTableValidator = dataTableValidator;
141-
this.columnValidator = columnValidator;
142-
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
143-
this.preventSqlInjectionService = sqlInjectionPreventerService;
144-
}
145-
146125
@Override
147126
public List<DatatableData> retrieveDatatableNames(final String appTable) {
148127
Object[] params = new Object[] { this.context.authenticatedUser().getId() };
@@ -306,14 +285,16 @@ private String getPermissionSql(final String dataTableName) {
306285
final String updatePermissionChecker = "'UPDATE_" + dataTableName + "_CHECKER'";
307286
final String deletePermission = "'DELETE_" + dataTableName + "'";
308287
final String deletePermissionChecker = "'DELETE_" + dataTableName + "_CHECKER'";
288+
final List<String> escapedColumns = Stream.of("grouping", "code", "action_name", "entity_name", "can_maker_checker")
289+
.map(sqlGenerator::escape).toList();
290+
final String columns = String.join(", ", escapedColumns);
309291

310-
return "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values " + "('datatable', "
311-
+ createPermission + ", 'CREATE', '" + dataTableName + "', true)," + "('datatable', " + createPermissionChecker
312-
+ ", 'CREATE', '" + dataTableName + "', false)," + "('datatable', " + readPermission + ", 'READ', '" + dataTableName
313-
+ "', false)," + "('datatable', " + updatePermission + ", 'UPDATE', '" + dataTableName + "', true)," + "('datatable', "
314-
+ updatePermissionChecker + ", 'UPDATE', '" + dataTableName + "', false)," + "('datatable', " + deletePermission
315-
+ ", 'DELETE', '" + dataTableName + "', true)," + "('datatable', " + deletePermissionChecker + ", 'DELETE', '"
316-
+ dataTableName + "', false)";
292+
return "insert into m_permission (" + columns + ") values " + "('datatable', " + createPermission + ", 'CREATE', '" + dataTableName
293+
+ "', true)," + "('datatable', " + createPermissionChecker + ", 'CREATE', '" + dataTableName + "', false),"
294+
+ "('datatable', " + readPermission + ", 'READ', '" + dataTableName + "', false)," + "('datatable', " + updatePermission
295+
+ ", 'UPDATE', '" + dataTableName + "', true)," + "('datatable', " + updatePermissionChecker + ", 'UPDATE', '"
296+
+ dataTableName + "', false)," + "('datatable', " + deletePermission + ", 'DELETE', '" + dataTableName + "', true),"
297+
+ "('datatable', " + deletePermissionChecker + ", 'DELETE', '" + dataTableName + "', false)";
317298

318299
}
319300

@@ -918,7 +899,7 @@ private void deleteColumnCodeMapping(final List<String> columnNames) {
918899
* Name of data table
919900
* @param column
920901
* JSON encoded array of column properties
921-
* @see https://mifosforge.jira.com/browse/MIFOSX-1145
902+
* @see <a href="https://mifosforge.jira.com/browse/MIFOSX-1145">MIFOSX-1145</a>
922903
**/
923904
private void removeNullValuesFromStringColumn(final String datatableName, final JsonObject column,
924905
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition) {

fineract-provider/src/main/java/org/apache/fineract/infrastructure/survey/service/WriteSurveyServiceImpl.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,25 @@
1818
*/
1919
package org.apache.fineract.infrastructure.survey.service;
2020

21+
import java.util.List;
22+
import java.util.stream.Stream;
23+
import lombok.RequiredArgsConstructor;
2124
import org.apache.fineract.infrastructure.core.api.JsonCommand;
2225
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
26+
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
2327
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
24-
import org.springframework.beans.factory.annotation.Autowired;
2528
import org.springframework.stereotype.Service;
2629
import org.springframework.transaction.annotation.Transactional;
2730

2831
/**
2932
* Created by Cieyou on 3/13/14.
3033
*/
3134
@Service
35+
@RequiredArgsConstructor
3236
public class WriteSurveyServiceImpl implements WriteSurveyService {
3337

3438
private final ReadWriteNonCoreDataService readWriteNonCoreDataService;
35-
36-
@Autowired(required = true)
37-
WriteSurveyServiceImpl(final ReadWriteNonCoreDataService readWriteNonCoreDataService) {
38-
this.readWriteNonCoreDataService = readWriteNonCoreDataService;
39-
40-
}
39+
private final DatabaseSpecificSQLGenerator sqlGenerator;
4140

4241
@Override
4342
@Transactional
@@ -59,14 +58,16 @@ private String getPermissionSql(final String dataTableName) {
5958
final String updatePermissionChecker = "'UPDATE_" + dataTableName + "_CHECKER'";
6059
final String deletePermission = "'DELETE_" + dataTableName + "'";
6160
final String deletePermissionChecker = "'DELETE_" + dataTableName + "_CHECKER'";
61+
final List<String> escapedColumns = Stream.of("grouping", "code", "action_name", "entity_name", "can_maker_checker")
62+
.map(sqlGenerator::escape).toList();
63+
final String columns = String.join(", ", escapedColumns);
6264

63-
return "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values " + "('datatable', "
64-
+ createPermission + ", 'CREATE', '" + dataTableName + "', false)," + "('datatable', " + createPermissionChecker
65-
+ ", 'CREATE', '" + dataTableName + "', false)," + "('datatable', " + readPermission + ", 'READ', '" + dataTableName
66-
+ "', false)," + "('datatable', " + updatePermission + ", 'UPDATE', '" + dataTableName + "', false)," + "('datatable', "
67-
+ updatePermissionChecker + ", 'UPDATE', '" + dataTableName + "', false)," + "('datatable', " + deletePermission
68-
+ ", 'DELETE', '" + dataTableName + "', false)," + "('datatable', " + deletePermissionChecker + ", 'DELETE', '"
69-
+ dataTableName + "', false)";
65+
return "insert into m_permission (" + columns + ") values " + "('datatable', " + createPermission + ", 'CREATE', '" + dataTableName
66+
+ "', false)," + "('datatable', " + createPermissionChecker + ", 'CREATE', '" + dataTableName + "', false),"
67+
+ "('datatable', " + readPermission + ", 'READ', '" + dataTableName + "', false)," + "('datatable', " + updatePermission
68+
+ ", 'UPDATE', '" + dataTableName + "', false)," + "('datatable', " + updatePermissionChecker + ", 'UPDATE', '"
69+
+ dataTableName + "', false)," + "('datatable', " + deletePermission + ", 'DELETE', '" + dataTableName + "', false),"
70+
+ "('datatable', " + deletePermissionChecker + ", 'DELETE', '" + dataTableName + "', false)";
7071
}
7172

7273
@Transactional

fineract-provider/src/main/resources/META-INF/orm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
<persistence-unit-metadata>
3030
<persistence-unit-defaults>
31+
<delimited-identifiers/>
3132
<entity-listeners>
3233
<entity-listener
3334
class="org.springframework.data.jpa.domain.support.AuditingEntityListener" />

integration-tests/build.gradle

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ apply from: 'dependencies.gradle'
3131
// enable when all tests are migrated
3232
tasks.cucumber.onlyIf {false}
3333

34+
// Allow external drivers to be used for the tests without packaging it
35+
// mainly due to license incompatibilities
36+
configurations {
37+
driver
38+
}
39+
dependencies {
40+
driver 'mysql:mysql-connector-java:8.0.29'
41+
}
42+
3443
cargo {
3544
containerId "tomcat9x"
3645

@@ -41,21 +50,28 @@ cargo {
4150
}
4251

4352
local {
44-
logLevel = 'medium'
53+
logLevel = 'low'
4554
// outputFile = file('build/output.log')
4655
installer {
4756
installConfiguration = configurations.tomcat
4857
downloadDir = file("$buildDir/download")
4958
extractDir = file("$buildDir/tomcat")
5059
}
5160
startStopTimeout = 240000
61+
sharedClasspath = configurations.driver
5262
containerProperties {
5363
def jvmArgs = '--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 '
5464
if (project.hasProperty('localDebug')) {
5565
jvmArgs += ' -agentlib:jdwp=transport=dt_socket,server=y,address=*:9000,suspend=n -Xmx2G -Duser.timezone=Asia/Kolkata '
5666
}
57-
if (project.hasProperty('dbType') && 'postgresql'.equalsIgnoreCase(dbType)) {
58-
jvmArgs += '-Dspring.datasource.hikari.driverClassName=org.postgresql.Driver -Dspring.datasource.hikari.jdbcUrl=jdbc:postgresql://localhost:5432/fineract_tenants -Dspring.datasource.hikari.username=root -Dspring.datasource.hikari.password=postgres -Dfineract.tenant.host=localhost -Dfineract.tenant.port=5432 -Dfineract.tenant.username=root -Dfineract.tenant.password=postgres'
67+
if (project.hasProperty('dbType')) {
68+
if ('postgresql'.equalsIgnoreCase(dbType)) {
69+
jvmArgs += '-Dspring.datasource.hikari.driverClassName=org.postgresql.Driver -Dspring.datasource.hikari.jdbcUrl=jdbc:postgresql://localhost:5432/fineract_tenants -Dspring.datasource.hikari.username=root -Dspring.datasource.hikari.password=postgres -Dfineract.tenant.host=localhost -Dfineract.tenant.port=5432 -Dfineract.tenant.username=root -Dfineract.tenant.password=postgres'
70+
} else if ('mysql'.equalsIgnoreCase(dbType)) {
71+
jvmArgs += '-Dspring.datasource.hikari.driverClassName=com.mysql.cj.jdbc.Driver -Dspring.datasource.hikari.jdbcUrl=jdbc:mysql://localhost:3306/fineract_tenants -Dspring.datasource.hikari.username=root -Dspring.datasource.hikari.password=mysql -Dfineract.tenant.host=localhost -Dfineract.tenant.port=3306 -Dfineract.tenant.username=root -Dfineract.tenant.password=mysql'
72+
} else {
73+
throw new GradleException('Provided dbType is not supported')
74+
}
5975
} else {
6076
jvmArgs += '-Dspring.datasource.hikari.driverClassName=org.mariadb.jdbc.Driver -Dspring.datasource.hikari.jdbcUrl=jdbc:mariadb://localhost:3306/fineract_tenants -Dspring.datasource.hikari.username=root -Dspring.datasource.hikari.password=mysql -Dfineract.tenant.host=localhost -Dfineract.tenant.port=3306 -Dfineract.tenant.username=root -Dfineract.tenant.password=mysql'
6177
}

0 commit comments

Comments
 (0)