This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 03e71a29f FINERACT-1996: Reporting should support query that includes 
json path for DB field in Postgres
03e71a29f is described below

commit 03e71a29ff8047ada340a66235731e7c35c1448a
Author: jmarta <[email protected]>
AuthorDate: Tue Oct 17 04:03:02 2023 +0200

    FINERACT-1996: Reporting should support query that includes json path for 
DB field in Postgres
---
 .../infrastructure/core/service/DateUtils.java     |   2 +
 .../database/DatabaseSpecificSQLGenerator.java     |  25 ++++-
 .../core/service/database/JdbcJavaType.java        |  15 ++-
 .../data/ResultsetColumnHeaderData.java            |   2 +-
 .../DatatableCommandFromApiJsonDeserializer.java   |   7 +-
 .../dataqueries/api/DataTableApiConstant.java      |   1 +
 .../service/GenericDataServiceImpl.java            |   2 +-
 .../service/ReadWriteNonCoreDataServiceImpl.java   |  10 +-
 .../ClientAuditingIntegrationTest.java             |  48 +++------
 .../LoanAuditingIntegrationTest.java               |  48 +++------
 .../LoanTransactionAuditingIntegrationTest.java    |  49 +++------
 .../fineract/integrationtests/common/Utils.java    |  18 ++--
 .../datatable/DatatableIntegrationTest.java        | 115 ++++++++++++---------
 13 files changed, 167 insertions(+), 175 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
index 6c5f518e1..d706e8fd5 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
@@ -193,6 +193,8 @@ public final class DateUtils {
         if (second == null) {
             return 1;
         }
+        first = first.withOffsetSameInstant(ZoneOffset.UTC);
+        second = second.withOffsetSameInstant(ZoneOffset.UTC);
         return truncate == null ? first.compareTo(second) : 
first.truncatedTo(truncate).compareTo(second.truncatedTo(truncate));
     }
 
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java
index 36d13697e..60f7aed1a 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java
@@ -23,8 +23,10 @@ import static java.lang.String.format;
 import jakarta.validation.constraints.NotNull;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import 
org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Sort;
@@ -251,18 +253,33 @@ public class DatabaseSpecificSQLGenerator {
                 .collect(Collectors.joining(", "));
     }
 
-    public String buildInsert(@NotNull String definition, Collection<String> 
fields) {
+    public String buildInsert(@NotNull String definition, List<String> fields, 
Map<String, ResultsetColumnHeaderData> headers) {
         if (fields == null || fields.isEmpty()) {
             return "";
         }
         return "INSERT INTO " + escape(definition) + '(' + 
fields.stream().map(this::escape).collect(Collectors.joining(", "))
-                + ") VALUES (?" + ", ?".repeat(fields.size() - 1) + ')';
+                + ") VALUES (" + fields.stream().map(e -> 
decoratePlaceHolder(headers, e, "?")).collect(Collectors.joining(", ")) + ')';
     }
 
-    public String buildUpdate(@NotNull String definition, Collection<String> 
fields) {
+    public String buildUpdate(@NotNull String definition, List<String> fields, 
Map<String, ResultsetColumnHeaderData> headers) {
         if (fields == null || fields.isEmpty()) {
             return "";
         }
-        return "UPDATE " + escape(definition) + " SET " + 
fields.stream().map(e -> escape(e) + " = ?").collect(Collectors.joining(", "));
+        return "UPDATE " + escape(definition) + " SET "
+                + fields.stream().map(e -> escape(e) + " = " + 
decoratePlaceHolder(headers, e, "?")).collect(Collectors.joining(", "));
+    }
+
+    private String decoratePlaceHolder(Map<String, ResultsetColumnHeaderData> 
headers, String field, String placeHolder) {
+        DatabaseType dialect = getDialect();
+        if (dialect.isPostgres()) {
+            ResultsetColumnHeaderData header = headers.get(field);
+            if (header != null) {
+                JdbcJavaType columnType = header.getColumnType();
+                if (columnType.isJsonType()) {
+                    return placeHolder + "::" + 
columnType.getJdbcName(dialect);
+                }
+            }
+        }
+        return placeHolder;
     }
 }
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/JdbcJavaType.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/JdbcJavaType.java
index 929debb92..aea1339f6 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/JdbcJavaType.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/JdbcJavaType.java
@@ -74,6 +74,7 @@ public enum JdbcJavaType {
     TINYTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "TINYTEXT"), 
new DialectType(JDBCType.VARCHAR, "TEXT")), //
     MEDIUMTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, 
"MEDIUMTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
     LONGTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "LONGTEXT"), 
new DialectType(JDBCType.VARCHAR, "TEXT")), //
+    JSON(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "JSON"), new 
DialectType(JDBCType.VARCHAR, "JSON")), //
     DATE(JavaType.LOCAL_DATE, new DialectType(JDBCType.DATE), new 
DialectType(JDBCType.DATE)), //
     // precision for TIME, TIMESTAMP (postgres) and INTERVAL specifies the 
number of fractional digits retained in the
     // seconds field, but by default, there is no explicit bound on precision
@@ -126,7 +127,7 @@ public enum JdbcJavaType {
         return javaType;
     }
 
-    public static JdbcJavaType getByTypeName(@NotNull DatabaseType dialect, 
String name) {
+    public static JdbcJavaType getByTypeName(@NotNull DatabaseType dialect, 
String name, boolean check) {
         if (name == null) {
             return null;
         }
@@ -145,6 +146,10 @@ public enum JdbcJavaType {
                 }
             }
         }
+        if (check) {
+            throw new 
PlatformServiceUnavailableException("error.msg.database.type.not.supported",
+                    "Data type '" + name + "' is not supported ");
+        }
         return null;
     }
 
@@ -179,7 +184,11 @@ public enum JdbcJavaType {
     }
 
     public boolean isTextType() {
-        return this == TEXT || this == TINYTEXT || this == MEDIUMTEXT || this 
== LONGTEXT;
+        return this == TEXT || this == TINYTEXT || this == MEDIUMTEXT || this 
== LONGTEXT || this == JSON;
+    }
+
+    public boolean isJsonType() {
+        return this == JSON;
     }
 
     public boolean isSerialType() {
@@ -262,7 +271,7 @@ public enum JdbcJavaType {
 
     public Object toJdbcValue(@NotNull DatabaseType dialect, Object value, 
boolean check) {
         if (value != null && check && 
!javaType.getObjectType().matchType(value.getClass(), false)) {
-            throw new 
PlatformServiceUnavailableException("error.msg.database.type.not.allowed",
+            throw new 
PlatformServiceUnavailableException("error.msg.database.type.not.valid",
                     "Data type of parameter " + value + " does not match " + 
this);
         }
         return toJdbcValueImpl(dialect, value);
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
index a1b930807..a28c92a91 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java
@@ -75,7 +75,7 @@ public final class ResultsetColumnHeaderData implements 
Serializable {
         this.isColumnIndexed = columnIsIndexed;
 
         // Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
-        this.columnType = JdbcJavaType.getByTypeName(dialect, 
adjustColumnType(columnType));
+        this.columnType = JdbcJavaType.getByTypeName(dialect, 
adjustColumnType(columnType), true);
 
         this.columnDisplayType = calcDisplayType();
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
index 0d2d96da5..4e300bda4 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java
@@ -23,6 +23,7 @@ import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaT
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATETIME;
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DECIMAL;
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.INTEGER;
+import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.JSON;
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.TEXT;
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.TIMESTAMP;
 import static 
org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.VARCHAR;
@@ -40,6 +41,7 @@ import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiCon
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DATETIME;
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DECIMAL;
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DROPDOWN;
+import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_JSON;
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_NUMBER;
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_STRING;
 import static 
org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_TEXT;
@@ -97,7 +99,8 @@ public class DatatableCommandFromApiJsonDeserializer {
             API_FIELD_MANDATORY, API_FIELD_AFTER, API_FIELD_CODE, 
API_FIELD_NEWCODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
     private static final Set<String> SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS = 
Set.of(API_FIELD_NAME);
     private static final Object[] SUPPORTED_COLUMN_TYPES = { 
API_FIELD_TYPE_STRING, API_FIELD_TYPE_NUMBER, API_FIELD_TYPE_BOOLEAN,
-            API_FIELD_TYPE_DECIMAL, API_FIELD_TYPE_DATE, 
API_FIELD_TYPE_DATETIME, API_FIELD_TYPE_TEXT, API_FIELD_TYPE_DROPDOWN };
+            API_FIELD_TYPE_DECIMAL, API_FIELD_TYPE_DATE, 
API_FIELD_TYPE_DATETIME, API_FIELD_TYPE_TEXT, API_FIELD_TYPE_JSON,
+            API_FIELD_TYPE_DROPDOWN };
 
     private final FromJsonHelper fromApiJsonHelper;
     private final DatabaseTypeResolver databaseTypeResolver;
@@ -344,6 +347,8 @@ public class DatatableCommandFromApiJsonDeserializer {
                 return TIMESTAMP;
             case API_FIELD_TYPE_TEXT:
                 return TEXT;
+            case API_FIELD_TYPE_JSON:
+                return JSON;
             default: {
                 if (fail) {
                     throw new 
PlatformDataIntegrityException("error.msg.datatable.column.type.invalid",
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java
index 0028c2070..85bb7fb8e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java
@@ -85,5 +85,6 @@ public final class DataTableApiConstant {
     public static final String API_FIELD_TYPE_DATETIME = "datetime";
     public static final String API_FIELD_TYPE_TIMESTAMP = "timestamp";
     public static final String API_FIELD_TYPE_TEXT = "text";
+    public static final String API_FIELD_TYPE_JSON = "json";
     public static final String API_FIELD_TYPE_DROPDOWN = "dropdown";
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
index 50722954c..9e58f044f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java
@@ -117,7 +117,7 @@ public class GenericDataServiceImpl implements 
GenericDataService {
             // primary keys and unique constrained columns are automatically 
indexed
             final boolean columnIsIndexed = columnIsPrimaryKey || 
columnIsUnique
                     || isExplicitlyIndexed(tableName, columnName, 
indexDefinitions);
-            JdbcJavaType jdbcType = JdbcJavaType.getByTypeName(dialect, 
columnType);
+            JdbcJavaType jdbcType = JdbcJavaType.getByTypeName(dialect, 
columnType, false);
 
             List<ResultsetColumnValueData> columnValues = new ArrayList<>();
             String codeName = null;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
index 37d63686e..205fdfef7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java
@@ -1323,7 +1323,6 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             params.add(SearchUtil.parseJdbcColumnValue(columnHeader, 
dataParams.get(key), dateFormat, dateTimeFormat, locale, false,
                     sqlGenerator));
         }
-        final String sql = sqlGenerator.buildInsert(dataTableName, 
insertColumns);
         if (addScore) {
             List<Object> scoreIds = params.stream().filter(e -> e != null && 
!String.valueOf(e).isBlank()).toList();
             int scoreValue;
@@ -1339,6 +1338,8 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
             insertColumns.add("score");
             params.add(scoreValue);
         }
+
+        final String sql = sqlGenerator.buildInsert(dataTableName, 
insertColumns, headersByName);
         try {
             int updated = jdbcTemplate.update(sql, 
params.toArray(Object[]::new));
             if (updated != 1) {
@@ -1446,8 +1447,8 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         Locale locale = localeString == null ? null : 
JsonParserHelper.localeFromString(localeString);
 
         DatabaseType dialect = sqlGenerator.getDialect();
-        List<String> updateColumns = new 
ArrayList<>(List.of(UPDATEDAT_FIELD_NAME));
-        List<Object> params = new 
ArrayList<>(List.of(DateUtils.getAuditLocalDateTime()));
+        ArrayList<String> updateColumns = new 
ArrayList<>(List.of(UPDATEDAT_FIELD_NAME));
+        ArrayList<Object> params = new 
ArrayList<>(List.of(DateUtils.getAuditLocalDateTime()));
         final HashMap<String, Object> changes = new HashMap<>();
         for (String key : dataParams.keySet()) {
             if (isTechnicalParam(key)) {
@@ -1474,7 +1475,8 @@ public class ReadWriteNonCoreDataServiceImpl implements 
ReadWriteNonCoreDataServ
         if (!updateColumns.isEmpty()) {
             ResultsetColumnHeaderData pkColumn = 
SearchUtil.getFiltered(columnHeaders, 
ResultsetColumnHeaderData::getIsColumnPrimaryKey);
             params.add(primaryKey);
-            final String sql = sqlGenerator.buildUpdate(dataTableName, 
updateColumns) + " WHERE " + pkColumn.getColumnName() + " = ?";
+            final String sql = sqlGenerator.buildUpdate(dataTableName, 
updateColumns, headersByName) + " WHERE " + pkColumn.getColumnName()
+                    + " = ?";
             int updated = jdbcTemplate.update(sql, 
params.toArray(Object[]::new));
             if (updated != 1) {
                 throw new 
PlatformDataIntegrityException("error.msg.invalid.update", "Expected one 
updated row.");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientAuditingIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientAuditingIntegrationTest.java
index 15d41a937..d66990803 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientAuditingIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientAuditingIntegrationTest.java
@@ -23,6 +23,7 @@ import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsCons
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY;
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
@@ -30,9 +31,10 @@ import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.time.OffsetDateTime;
-import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.Map;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
@@ -65,12 +67,7 @@ public class ClientAuditingIntegrationTest {
         String username = Utils.uniqueRandomStringGenerator("user", 8);
         final Integer userId = (Integer) 
UserHelper.createUser(this.requestSpec, this.responseSpec, 1, staffId, 
username, "password",
                 "resourceId");
-        OffsetDateTime now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        // Testing in minutes precision, but still need to take care around 
the end of the actual minute
-        if (now.getSecond() > 56) {
-            Thread.sleep(5000);
-            now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        }
+        OffsetDateTime now = Utils.getAuditDateTimeToCompare();
         LOG.info("-------------------------Creating 
Client---------------------------");
 
         final Integer clientID = ClientHelper.createClientPending(requestSpec, 
responseSpec);
@@ -79,55 +76,36 @@ public class ClientAuditingIntegrationTest {
 
         OffsetDateTime createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
         OffsetDateTime lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
         assertEquals(1, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now, createdDate, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(now, lastModifiedDate, 
ChronoUnit.MINUTES));
 
         LOG.info("-------------------------Modify Client with System 
user---------------------------");
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization",
                 "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey(username, 
"password"));
-
         this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+
+        OffsetDateTime now2 = Utils.getAuditDateTimeToCompare();
         this.clientHelper.activateClient(clientID);
         auditFieldsResponse = ClientHelper.getClientAuditFields(requestSpec, 
responseSpec, clientID, "");
 
-        createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
+        OffsetDateTime createdDate2 = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
+                DateTimeFormatter.ISO_OFFSET_DATE_TIME);
         lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
-        now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
+        assertTrue(DateUtils.isEqual(now, createdDate2, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(createdDate, createdDate2));
 
         assertEquals(userId, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now2, lastModifiedDate, 
ChronoUnit.MINUTES));
     }
-
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
index 318ec3774..f36d51827 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
@@ -23,6 +23,7 @@ import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsCons
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY;
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
@@ -30,12 +31,13 @@ import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.time.OffsetDateTime;
-import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.accounting.Account;
@@ -93,12 +95,8 @@ public class LoanAuditingIntegrationTest {
 
         final Integer loanProductID = createLoanProduct("0", "0", 
LoanProductTestBuilder.DEFAULT_STRATEGY, "2", assetAccount, incomeAccount,
                 expenseAccount, overpaymentAccount);
-        OffsetDateTime now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        // Testing in minutes precision, but still need to take care around 
the end of the actual minute
-        if (now.getSecond() > 56) {
-            Thread.sleep(5000);
-            now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        }
+        OffsetDateTime now = Utils.getAuditDateTimeToCompare();
+
         final Integer loanID = 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, 
Collections.emptyList(),
                 null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, 
"10 July 2022");
         Assertions.assertNotNull(loanID);
@@ -109,57 +107,39 @@ public class LoanAuditingIntegrationTest {
 
         OffsetDateTime createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
         OffsetDateTime lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
         assertEquals(1, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now, createdDate, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(now, lastModifiedDate, 
ChronoUnit.MINUTES));
 
         LOG.info("-----------------------------------APPROVE 
LOAN-----------------------------------------");
 
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization",
                 "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey(username, 
"P4ssw0rd"));
-
         this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
+        OffsetDateTime now2 = Utils.getAuditDateTimeToCompare();
         loanStatusHashMap = this.loanTransactionHelper.approveLoan("11 July 
2022", loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
         auditFieldsResponse = 
LoanTransactionHelper.getLoanAuditFields(requestSpec, responseSpec, loanID, "");
 
-        createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
+        OffsetDateTime createdDate2 = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
+                DateTimeFormatter.ISO_OFFSET_DATE_TIME);
         lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
-        now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
+        assertTrue(DateUtils.isEqual(now, createdDate2, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(createdDate, createdDate2));
 
         assertEquals(userId, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now2, lastModifiedDate, 
ChronoUnit.MINUTES));
     }
 
     private Integer 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, 
final Integer loanProductID,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
index aadabadee..c1a4e5f47 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
@@ -23,6 +23,7 @@ import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsCons
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY;
 import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
@@ -30,11 +31,12 @@ import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.time.OffsetDateTime;
-import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.accounting.Account;
@@ -105,12 +107,7 @@ public class LoanTransactionAuditingIntegrationTest {
         loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("11 July 2022", 
loanID, "10000");
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
-        OffsetDateTime now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        // Testing in minutes precision, but still need to take care around 
the end of the actual minute
-        if (now.getSecond() > 56) {
-            Thread.sleep(5000);
-            now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
-        }
+        OffsetDateTime now = Utils.getAuditDateTimeToCompare();
         HashMap repaymentDetails = 
this.loanTransactionHelper.makeRepayment("11 July 2022", 100.0f, loanID);
         Integer transactionId = (Integer) repaymentDetails.get("resourceId");
         HashMap auditFieldsResponse = 
LoanTransactionHelper.getLoanTransactionAuditFields(requestSpec, responseSpec, 
loanID, transactionId,
@@ -118,59 +115,39 @@ public class LoanTransactionAuditingIntegrationTest {
 
         OffsetDateTime createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
         OffsetDateTime lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
         assertEquals(1, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now, createdDate, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(now, lastModifiedDate, 
ChronoUnit.MINUTES));
 
         Thread.sleep(2000);
 
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization",
                 "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey(username, 
"P4ssw0rd"));
-
         this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.loanTransactionHelper.reverseRepayment(loanID, transactionId, "11 
July 2022");
 
-        now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
+        OffsetDateTime now2 = Utils.getAuditDateTimeToCompare();
+        this.loanTransactionHelper.reverseRepayment(loanID, transactionId, "11 
July 2022");
 
         auditFieldsResponse = 
LoanTransactionHelper.getLoanTransactionAuditFields(requestSpec, responseSpec, 
loanID, transactionId, "");
 
-        createdDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
-
+        OffsetDateTime createdDate2 = OffsetDateTime.parse((String) 
auditFieldsResponse.get(CREATED_DATE),
+                DateTimeFormatter.ISO_OFFSET_DATE_TIME);
         lastModifiedDate = OffsetDateTime.parse((String) 
auditFieldsResponse.get(LAST_MODIFIED_DATE),
                 DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
         LOG.info("-------------------------Check Audit 
dates---------------------------");
         assertEquals(1, auditFieldsResponse.get(CREATED_BY));
-        assertEquals(now.getYear(), createdDate.getYear());
-        assertEquals(now.getMonth(), createdDate.getMonth());
-        assertEquals(now.getDayOfMonth(), createdDate.getDayOfMonth());
-        assertEquals(now.getHour(), createdDate.getHour());
-        assertEquals(now.getMinute(), createdDate.getMinute());
-
-        now = OffsetDateTime.now(ZoneId.of("Asia/Kolkata"));
+        assertTrue(DateUtils.isEqual(now, createdDate2, ChronoUnit.MINUTES));
+        assertTrue(DateUtils.isEqual(createdDate, createdDate2));
 
         assertEquals(userId, auditFieldsResponse.get(LAST_MODIFIED_BY));
-        assertEquals(now.getYear(), lastModifiedDate.getYear());
-        assertEquals(now.getMonth(), lastModifiedDate.getMonth());
-        assertEquals(now.getDayOfMonth(), lastModifiedDate.getDayOfMonth());
-        assertEquals(now.getHour(), lastModifiedDate.getHour());
-        assertEquals(now.getMinute(), lastModifiedDate.getMinute());
+        assertTrue(DateUtils.isEqual(now2, lastModifiedDate, 
ChronoUnit.MINUTES));
     }
 
     private Integer 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, 
final Integer loanProductID,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
index b9738cf73..5fc131b0e 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/Utils.java
@@ -44,7 +44,6 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.time.ZoneId;
-import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.temporal.ChronoField;
@@ -63,7 +62,9 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.http.conn.HttpHostConnectException;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -376,12 +377,15 @@ public final class Utils {
         return dateFormat.format(dateToBeConvert.getTime());
     }
 
-    public static OffsetDateTime getAuditOffsetDateTime() {
-        return OffsetDateTime.now(ZoneOffset.UTC);
-    }
-
-    public static LocalDateTime getLocalDateTimeOfSystem() {
-        return LocalDateTime.now(ZoneId.systemDefault());
+    @NotNull
+    public static OffsetDateTime getAuditDateTimeToCompare() throws 
InterruptedException {
+        OffsetDateTime now = DateUtils.getAuditOffsetDateTime();
+        // Testing in minutes precision, but still need to take care around 
the end of the actual minute
+        if (now.getSecond() > 56) {
+            Thread.sleep(5000);
+            now = DateUtils.getAuditOffsetDateTime();
+        }
+        return now;
     }
 
     public static TimeZone getTimeZoneOfTenant() {
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/datatable/DatatableIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/datatable/DatatableIntegrationTest.java
index 2c7b8a4bc..3c9bdc4f7 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/datatable/DatatableIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/datatable/DatatableIntegrationTest.java
@@ -139,6 +139,7 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
         String itsANumber = "itsanumber";
         String itsAString = "itsastring";
         String itsAText = "itsatext";
+        String itsAJson = "itsajson";
         String tst_tst_tst_cd_itsADropdown = tst_tst_tst + "_cd_itsadropdown";
         String dateFormat = "dateFormat";
 
@@ -155,43 +156,45 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
         columnMap.put("apptableName", null);
         String errorRequestJsonString = new Gson().toJson(columnMap);
         ResponseSpecification responseSpecError400 = new 
ResponseSpecBuilder().expectStatusCode(400).build();
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
-        HashMap<String, Object> errorResponse = 
this.datatableHelper.createDatatable(errorRequestJsonString, "");
+        DatatableHelper error400Helper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
+        HashMap<String, Object> errorResponse = 
error400Helper.createDatatable(errorRequestJsonString, "");
         assertEquals("validation.msg.validation.errors.exist", ((Map) 
errorResponse).get("userMessageGlobalisationCode"));
         List errors = (List) ((Map) errorResponse).get("errors");
         assertEquals(2, errors.size());
         assertEquals("validation.msg.datatable.apptableName.cannot.be.blank", 
((Map) errors.get(0)).get("userMessageGlobalisationCode"));
         
assertEquals("validation.msg.datatable.apptableName.is.not.one.of.expected.enumerations",
                 ((Map) errors.get(1)).get("userMessageGlobalisationCode"));
+
         // set valid apptable name
         columnMap.put("apptableName", CLIENT_APP_TABLE_NAME);
 
         // try to create datatable with invalid column type
         HashMap<String, Object> textColumn = 
addDatatableColumn(datatableColumnsList, itsAText, "Invalid", true, null, null);
         errorRequestJsonString = new Gson().toJson(columnMap);
-        errorResponse = 
this.datatableHelper.createDatatable(errorRequestJsonString, "");
+        errorResponse = error400Helper.createDatatable(errorRequestJsonString, 
"");
         assertEquals("validation.msg.validation.errors.exist", ((Map) 
errorResponse).get("userMessageGlobalisationCode"));
         errors = (List) ((Map) errorResponse).get("errors");
         assertEquals(1, errors.size());
         Map error = (Map) errors.get(0);
         
assertEquals("validation.msg.datatable.type.is.not.one.of.expected.enumerations",
 error.get("userMessageGlobalisationCode"));
-        assertTrue(((String) 
error.get("defaultUserMessage")).contains("string, number, boolean, decimal, 
date, datetime, text, dropdown"));
+        assertTrue(((String) error.get("defaultUserMessage"))
+                .contains("string, number, boolean, decimal, date, datetime, 
text, json, dropdown"));
+
         // set valid type
         textColumn.put("type", "Text");
+        // add json type
+        addDatatableColumn(datatableColumnsList, itsAJson, "Json", false, 
null, null);
 
         String datatabelRequestJsonString = new Gson().toJson(columnMap);
         LOG.info("map : {}", datatabelRequestJsonString);
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
 
         HashMap<String, Object> datatableResponse = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
         String datatableName = (String) 
datatableResponse.get("resourceIdentifier");
         DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, 
this.responseSpec, datatableName);
 
         // try to create with the same name
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
-        errorResponse = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        errorResponse = 
error400Helper.createDatatable(datatabelRequestJsonString, "");
         assertEquals("validation.msg.validation.errors.exist", ((Map) 
errorResponse).get("userMessageGlobalisationCode"));
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
 
         // creating client with datatables
         final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
@@ -211,7 +214,19 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
         datatableEntryMap.put("locale", "en");
         datatableEntryMap.put(dateFormat, "yyyy-MM-dd");
 
+        String json = "{\"testparam\": \"testvalue\"}";
+        // add invalid json
+        datatableEntryMap.put(itsAJson, '{' + json);
+
         String datatabelEntryRequestJsonString = new 
Gson().toJson(datatableEntryMap);
+        ResponseSpecification responseSpecError403 = new 
ResponseSpecBuilder().expectStatusCode(403).build();
+        DatatableHelper error403Helper = new DatatableHelper(this.requestSpec, 
responseSpecError403);
+        errorResponse = error403Helper.createDatatableEntry(datatableName, 
clientID, genericResultSet, datatabelEntryRequestJsonString);
+
+        // add valid json
+        datatableEntryMap.put(itsAJson, json);
+
+        datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
         LOG.info("map : {}", datatabelEntryRequestJsonString);
 
         HashMap<String, Object> datatableEntryResponse = 
this.datatableHelper.createDatatableEntry(datatableName, clientID,
@@ -222,37 +237,43 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
         final HashMap<String, Object> items = 
this.datatableHelper.readDatatableEntry(datatableName, clientID, 
genericResultSet,
                 (Integer) datatableEntryResponse.get("resourceId"), "");
         assertNotNull(items);
-        assertEquals(1, ((List) items.get("data")).size());
 
-        assertEquals("client_id", ((Map) ((List) 
items.get("columnHeaders")).get(0)).get("columnName"));
-        assertEquals(clientID, ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(0));
+        List columnHeaders = (List) items.get("columnHeaders");
+        List columnData = (List) items.get("data");
+        assertEquals(1, columnData.size());
 
-        assertEquals(itsABoolean, ((Map) ((List) 
items.get("columnHeaders")).get(1)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsABoolean), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(1));
+        Map data = (Map) columnData.get(0);
 
-        assertEquals(itsADate, ((Map) ((List) 
items.get("columnHeaders")).get(2)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsADate),
-                Utils.arrayDateToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(2)));
+        assertEquals("client_id", ((Map) 
columnHeaders.get(0)).get("columnName"));
+        assertEquals(clientID, ((List) data.get("row")).get(0));
 
-        assertEquals(itsADatetime, ((Map) ((List) 
items.get("columnHeaders")).get(3)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsADatetime),
-                Utils.arrayDateTimeToString((List) ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(3)));
+        assertEquals(itsABoolean, ((Map) 
columnHeaders.get(1)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsABoolean), ((List) 
data.get("row")).get(1));
 
-        assertEquals(itsADecimal, ((Map) ((List) 
items.get("columnHeaders")).get(4)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsADecimal), ((List) ((Map) 
((List) items.get("data")).get(0)).get("row")).get(4));
+        assertEquals(itsADate, ((Map) columnHeaders.get(2)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsADate), 
Utils.arrayDateToString((List) ((List) data.get("row")).get(2)));
 
-        assertEquals(tst_tst_tst_cd_itsADropdown, ((Map) ((List) 
items.get("columnHeaders")).get(5)).get("columnName"));
-        assertEquals(datatableEntryMap.get(tst_tst_tst_cd_itsADropdown),
-                ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(5));
+        assertEquals(itsADatetime, ((Map) 
columnHeaders.get(3)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsADatetime), 
Utils.arrayDateTimeToString((List) ((List) data.get("row")).get(3)));
+
+        assertEquals(itsADecimal, ((Map) 
columnHeaders.get(4)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsADecimal), ((List) 
data.get("row")).get(4));
+
+        assertEquals(tst_tst_tst_cd_itsADropdown, ((Map) 
columnHeaders.get(5)).get("columnName"));
+        assertEquals(datatableEntryMap.get(tst_tst_tst_cd_itsADropdown), 
((List) data.get("row")).get(5));
+
+        assertEquals(itsANumber, ((Map) 
columnHeaders.get(6)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsANumber), ((List) 
data.get("row")).get(6));
 
-        assertEquals(itsANumber, ((Map) ((List) 
items.get("columnHeaders")).get(6)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsANumber), ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(6));
+        assertEquals(itsAString, ((Map) 
columnHeaders.get(7)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsAString), ((List) 
data.get("row")).get(7));
 
-        assertEquals(itsAString, ((Map) ((List) 
items.get("columnHeaders")).get(7)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsAString), ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(7));
+        assertEquals(itsAText, ((Map) columnHeaders.get(8)).get("columnName"));
+        assertEquals(datatableEntryMap.get(itsAText), ((List) 
data.get("row")).get(8));
 
-        assertEquals(itsAText, ((Map) ((List) 
items.get("columnHeaders")).get(8)).get("columnName"));
-        assertEquals(datatableEntryMap.get(itsAText), ((List) ((Map) ((List) 
items.get("data")).get(0)).get("row")).get(8));
+        assertEquals(itsAJson, ((Map) columnHeaders.get(9)).get("columnName"));
+        Object jsonResponse = ((List) data.get("row")).get(9);
+        assertEquals(datatableEntryMap.get(itsAJson), jsonResponse instanceof 
Map ? ((Map) jsonResponse).get("value") : jsonResponse);
 
         // Read the Datatable entry generated with genericResultSet in false
         List<HashMap<String, Object>> datatableEntryResponseNoGenericResult = 
this.datatableHelper.readDatatableEntry(datatableName,
@@ -260,19 +281,17 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
         assertNotNull(datatableEntryResponseNoGenericResult, "ERROR IN GETTING 
THE DATE VALUE FROM DATATABLE RECORD");
         assertEquals(1, datatableEntryResponseNoGenericResult.size());
 
-        assertEquals(clientID, 
datatableEntryResponseNoGenericResult.get(0).get("client_id"));
-        assertEquals(datatableEntryMap.get(itsABoolean),
-                Boolean.valueOf((String) 
datatableEntryResponseNoGenericResult.get(0).get(itsABoolean)));
-        assertEquals(datatableEntryMap.get(itsADate),
-                Utils.arrayDateToString((List) 
datatableEntryResponseNoGenericResult.get(0).get(itsADate)));
-        assertEquals(datatableEntryMap.get(itsADecimal), 
datatableEntryResponseNoGenericResult.get(0).get(itsADecimal));
-        assertEquals(datatableEntryMap.get(itsADatetime),
-                Utils.arrayDateTimeToString((List<Integer>) 
datatableEntryResponseNoGenericResult.get(0).get(itsADatetime)));
-        assertEquals(datatableEntryMap.get(tst_tst_tst_cd_itsADropdown),
-                
datatableEntryResponseNoGenericResult.get(0).get(tst_tst_tst_cd_itsADropdown));
-        assertEquals(datatableEntryMap.get(itsANumber), 
datatableEntryResponseNoGenericResult.get(0).get(itsANumber));
-        assertEquals(datatableEntryMap.get(itsAString), 
datatableEntryResponseNoGenericResult.get(0).get(itsAString));
-        assertEquals(datatableEntryMap.get(itsAText), 
datatableEntryResponseNoGenericResult.get(0).get(itsAText));
+        HashMap<String, Object> responseMap = 
datatableEntryResponseNoGenericResult.get(0);
+        assertEquals(clientID, responseMap.get("client_id"));
+        assertEquals(datatableEntryMap.get(itsABoolean), 
Boolean.valueOf((String) responseMap.get(itsABoolean)));
+        assertEquals(datatableEntryMap.get(itsADate), 
Utils.arrayDateToString((List) responseMap.get(itsADate)));
+        assertEquals(datatableEntryMap.get(itsADecimal), 
responseMap.get(itsADecimal));
+        assertEquals(datatableEntryMap.get(itsADatetime), 
Utils.arrayDateTimeToString((List<Integer>) responseMap.get(itsADatetime)));
+        assertEquals(datatableEntryMap.get(tst_tst_tst_cd_itsADropdown), 
responseMap.get(tst_tst_tst_cd_itsADropdown));
+        assertEquals(datatableEntryMap.get(itsANumber), 
responseMap.get(itsANumber));
+        assertEquals(datatableEntryMap.get(itsAString), 
responseMap.get(itsAString));
+        assertEquals(datatableEntryMap.get(itsAText), 
responseMap.get(itsAText));
+        assertEquals(datatableEntryMap.get(itsAJson), 
responseMap.get(itsAJson));
 
         // Update datatable entry
         Boolean previousBoolean = (Boolean) datatableEntryMap.get(itsABoolean);
@@ -475,10 +494,9 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
 
         // try to create with the same name
         ResponseSpecification responseSpecError400 = new 
ResponseSpecBuilder().expectStatusCode(400).build();
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
-        HashMap<String, Object> response = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        DatatableHelper error400Helper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
+        HashMap<String, Object> response = 
error400Helper.createDatatable(datatabelRequestJsonString, "");
         assertEquals("validation.msg.validation.errors.exist", ((Map) 
response).get("userMessageGlobalisationCode"));
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
 
         // creating client with datatables
         final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);
@@ -739,10 +757,9 @@ public class DatatableIntegrationTest extends 
IntegrationTest {
 
         // try to create with the same name
         ResponseSpecification responseSpecError400 = new 
ResponseSpecBuilder().expectStatusCode(400).build();
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
-        HashMap<String, Object> response = 
this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
+        DatatableHelper error400Helper = new DatatableHelper(this.requestSpec, 
responseSpecError400);
+        HashMap<String, Object> response = 
error400Helper.createDatatable(datatabelRequestJsonString, "");
         assertEquals("validation.msg.validation.errors.exist", ((Map) 
response).get("userMessageGlobalisationCode"));
-        this.datatableHelper = new DatatableHelper(this.requestSpec, 
this.responseSpec);
 
         // creating client with datatables
         final Integer clientID = 
ClientHelper.createClientAsPerson(requestSpec, responseSpec);

Reply via email to