FINERACT-1911: Assign Data Table to Transaction - general
This commit is contained in:
+191
-157
@@ -18,13 +18,48 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.serialization;
|
||||
|
||||
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.BOOLEAN;
|
||||
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATE;
|
||||
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.TEXT;
|
||||
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.VARCHAR;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_AFTER;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_CODE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_INDEXED;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_LENGTH;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_MANDATORY;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NAME;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NEWCODE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NEWNAME;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_BOOLEAN;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DATE;
|
||||
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_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;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_TIMESTAMP;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_UNIQUE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_ADDCOLUMNS;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_APPTABLE_NAME;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_CHANGECOLUMNS;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_COLUMNS;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_DATATABLE_NAME;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_DROPCOLUMNS;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_MULTIROW;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_SUBTYPE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.TABLE_FIELD_ID;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -34,6 +69,8 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -41,90 +78,33 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class DatatableCommandFromApiJsonDeserializer {
|
||||
|
||||
public static final String DATATABLE_NAME = "datatableName";
|
||||
public static final String ENTITY_SUB_TYPE = "entitySubType";
|
||||
public static final String APPTABLE_NAME = "apptableName";
|
||||
public static final String MULTI_ROW = "multiRow";
|
||||
public static final String COLUMNS = "columns";
|
||||
public static final String NAME = "name";
|
||||
public static final String TYPE = "type";
|
||||
public static final String LENGTH = "length";
|
||||
public static final String MANDATORY = "mandatory";
|
||||
public static final String CODE = "code";
|
||||
public static final String CHANGE_COLUMNS = "changeColumns";
|
||||
public static final String ADD_COLUMNS = "addColumns";
|
||||
public static final String DROP_COLUMNS = "dropColumns";
|
||||
public static final String AFTER = "after";
|
||||
public static final String NEW_CODE = "newCode";
|
||||
public static final String NEW_NAME = "newName";
|
||||
public static final String STRING = "string";
|
||||
public static final String NUMBER = "number";
|
||||
public static final String BOOLEAN = "boolean";
|
||||
public static final String DECIMAL = "decimal";
|
||||
public static final String DATE = "date";
|
||||
public static final String DATETIME = "datetime";
|
||||
public static final String TEXT = "text";
|
||||
public static final String DROPDOWN = "dropdown";
|
||||
private static final String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
|
||||
private static final String DATATABLE_COLUMN_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,}[a-zA-Z0-9]$";
|
||||
private static final String INDEXED = "indexed";
|
||||
private static final String UNIQUE = "unique";
|
||||
public static final String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
|
||||
public static final String DATATABLE_COLUMN_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,}[a-zA-Z0-9]$";
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE = new HashSet<>(
|
||||
Arrays.asList(DATATABLE_NAME, ENTITY_SUB_TYPE, APPTABLE_NAME, MULTI_ROW, COLUMNS));
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE_COLUMNS = new HashSet<>(
|
||||
Arrays.asList(NAME, TYPE, LENGTH, MANDATORY, CODE, UNIQUE, INDEXED));
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_UPDATE = new HashSet<>(
|
||||
Arrays.asList(APPTABLE_NAME, ENTITY_SUB_TYPE, CHANGE_COLUMNS, ADD_COLUMNS, DROP_COLUMNS));
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_ADD_COLUMNS = new HashSet<>(
|
||||
Arrays.asList(NAME, TYPE, LENGTH, MANDATORY, AFTER, CODE, UNIQUE, INDEXED));
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CHANGE_COLUMNS = new HashSet<>(
|
||||
Arrays.asList(NAME, NEW_NAME, LENGTH, MANDATORY, AFTER, CODE, NEW_CODE, UNIQUE, INDEXED));
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS = new HashSet<>(List.of(NAME));
|
||||
private static final Object[] SUPPORTED_COLUMN_TYPES = { STRING, NUMBER, BOOLEAN, DECIMAL, DATE, DATETIME, TEXT, DROPDOWN };
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE = Set.of(API_PARAM_DATATABLE_NAME, API_PARAM_SUBTYPE,
|
||||
API_PARAM_APPTABLE_NAME, API_PARAM_MULTIROW, API_PARAM_COLUMNS);
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_TYPE, API_FIELD_LENGTH,
|
||||
API_FIELD_MANDATORY, API_FIELD_CODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_UPDATE = Set.of(API_PARAM_APPTABLE_NAME, API_PARAM_SUBTYPE,
|
||||
API_PARAM_CHANGECOLUMNS, API_PARAM_ADDCOLUMNS, API_PARAM_DROPCOLUMNS);
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_ADD_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_TYPE, API_FIELD_LENGTH,
|
||||
API_FIELD_MANDATORY, API_FIELD_AFTER, API_FIELD_CODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
|
||||
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CHANGE_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_NEWNAME, API_FIELD_LENGTH,
|
||||
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 };
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DatabaseTypeResolver databaseTypeResolver;
|
||||
|
||||
@Autowired
|
||||
public DatatableCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
|
||||
public DatatableCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper, DatabaseTypeResolver databaseTypeResolver) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
private void validateType(final DataValidatorBuilder baseDataValidator, final JsonElement column) {
|
||||
final String type = this.fromApiJsonHelper.extractStringNamed(TYPE, column);
|
||||
baseDataValidator.reset().parameter(TYPE).value(type).notBlank().isOneOfTheseStringValues(SUPPORTED_COLUMN_TYPES);
|
||||
|
||||
if (type != null && type.equalsIgnoreCase("String")) {
|
||||
if (this.fromApiJsonHelper.parameterExists(LENGTH, column)) {
|
||||
final String lengthStr = this.fromApiJsonHelper.extractStringNamed(LENGTH, column);
|
||||
if (lengthStr != null && !StringUtils.isWhitespace(lengthStr) && StringUtils.isNumeric(lengthStr)
|
||||
&& StringUtils.isNotBlank(lengthStr)) {
|
||||
final Integer length = Integer.parseInt(lengthStr);
|
||||
baseDataValidator.reset().parameter(LENGTH).value(length).positiveAmount();
|
||||
} else if (StringUtils.isBlank(lengthStr) || StringUtils.isWhitespace(lengthStr)) {
|
||||
baseDataValidator.reset().parameter(LENGTH).failWithCode("must.be.provided.when.type.is.String");
|
||||
} else if (!StringUtils.isNumeric(lengthStr)) {
|
||||
baseDataValidator.reset().parameter(LENGTH).failWithCode("not.greater.than.zero");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LENGTH).failWithCode("must.be.provided.when.type.is.String");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LENGTH).mustBeBlankWhenParameterProvidedIs(TYPE, type);
|
||||
}
|
||||
|
||||
final String code = this.fromApiJsonHelper.extractStringNamed(CODE, column);
|
||||
if (type != null && type.equalsIgnoreCase(DROPDOWN)) {
|
||||
if (code != null) {
|
||||
baseDataValidator.reset().parameter(CODE).value(code).notBlank().matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(CODE).value(code).cantBeBlankWhenParameterProvidedIs(TYPE, type);
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(CODE).value(code).mustBeBlankWhenParameterProvided(TYPE, type);
|
||||
}
|
||||
this.databaseTypeResolver = databaseTypeResolver;
|
||||
}
|
||||
|
||||
public void validateForCreate(final String json) {
|
||||
@@ -132,9 +112,7 @@ public class DatatableCommandFromApiJsonDeserializer {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
|
||||
}.getType();
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS_FOR_CREATE);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
@@ -142,53 +120,50 @@ public class DatatableCommandFromApiJsonDeserializer {
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String datatableName = this.fromApiJsonHelper.extractStringNamed(DATATABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(DATATABLE_NAME).value(datatableName).notBlank().notExceedingLengthOf(50)
|
||||
final String datatableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_DATATABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_DATATABLE_NAME).value(datatableName).notBlank().notExceedingLengthOf(50)
|
||||
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
|
||||
|
||||
final String apptableName = this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).notBlank().notExceedingLengthOf(50)
|
||||
.isOneOfTheseStringValues(EntityTables.getEntitiesList());
|
||||
final String apptableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_APPTABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_APPTABLE_NAME).value(apptableName).notBlank().notExceedingLengthOf(50)
|
||||
.isOneOfTheseStringValues(EntityTables.getEntityNames());
|
||||
|
||||
validateEntitySubType(baseDataValidator, element, apptableName);
|
||||
final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : "";
|
||||
EntityTables entityTable = EntityTables.fromEntityName(apptableName);
|
||||
validateEntitySubType(baseDataValidator, element, entityTable);
|
||||
|
||||
final Boolean multiRow = this.fromApiJsonHelper.extractBooleanNamed(MULTI_ROW, element);
|
||||
baseDataValidator.reset().parameter(MULTI_ROW).value(multiRow).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final String fkColumnName = entityTable.getForeignKeyColumnNameOnDatatable();
|
||||
|
||||
final JsonArray columns = this.fromApiJsonHelper.extractJsonArrayNamed(COLUMNS, element);
|
||||
baseDataValidator.reset().parameter(COLUMNS).value(columns).notNull().jsonArrayNotEmpty();
|
||||
final Boolean multiRow = this.fromApiJsonHelper.extractBooleanNamed(API_PARAM_MULTIROW, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_MULTIROW).value(multiRow).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
|
||||
final JsonArray columns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_COLUMNS, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_COLUMNS).value(columns).notNull().jsonArrayNotEmpty();
|
||||
|
||||
if (columns != null) {
|
||||
for (final JsonElement column : columns) {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_CREATE_COLUMNS);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, column);
|
||||
baseDataValidator.reset().parameter(NAME).value(name).notBlank().isNotOneOfTheseValues("id", fkColumnName)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
|
||||
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
validateType(baseDataValidator, column);
|
||||
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(MANDATORY, column);
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(UNIQUE, column);
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(INDEXED, column);
|
||||
baseDataValidator.reset().parameter(MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
baseDataValidator.reset().parameter(UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
baseDataValidator.reset().parameter(INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void validateEntitySubType(final DataValidatorBuilder baseDataValidator, final JsonElement element, final String apptableName) {
|
||||
EntityTables entityTable = EntityTables.fromName(apptableName);
|
||||
if (entityTable != null && EntityTables.CLIENT == entityTable) {
|
||||
String entitySubType = this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element);
|
||||
baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank(); // Person or Entity
|
||||
}
|
||||
}
|
||||
|
||||
public void validateForUpdate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
@@ -211,108 +186,167 @@ public class DatatableCommandFromApiJsonDeserializer {
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final String apptableName = this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).ignoreIfNull().notBlank()
|
||||
.isOneOfTheseStringValues(EntityTables.getEntitiesList());
|
||||
final String apptableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_APPTABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_APPTABLE_NAME).value(apptableName).ignoreIfNull().notBlank()
|
||||
.isOneOfTheseStringValues(EntityTables.getEntityNames());
|
||||
|
||||
validateEntitySubType(baseDataValidator, element, apptableName);
|
||||
EntityTables entityTable = EntityTables.fromEntityName(apptableName);
|
||||
validateEntitySubType(baseDataValidator, element, entityTable);
|
||||
|
||||
final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : "";
|
||||
final String fkColumnName = entityTable.getForeignKeyColumnNameOnDatatable();
|
||||
|
||||
final JsonArray changeColumns = this.fromApiJsonHelper.extractJsonArrayNamed(CHANGE_COLUMNS, element);
|
||||
baseDataValidator.reset().parameter(CHANGE_COLUMNS).value(changeColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
final JsonArray changeColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_CHANGECOLUMNS, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_CHANGECOLUMNS).value(changeColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
|
||||
if (changeColumns != null) {
|
||||
for (final JsonElement column : changeColumns) {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_CHANGE_COLUMNS);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, column);
|
||||
baseDataValidator.reset().parameter(NAME).value(name).notBlank().isNotOneOfTheseValues("id", fkColumnName)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
|
||||
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
final String newName = this.fromApiJsonHelper.extractStringNamed(NEW_NAME, column);
|
||||
baseDataValidator.reset().parameter(NEW_NAME).value(newName).ignoreIfNull().notBlank().notExceedingLengthOf(50)
|
||||
.isNotOneOfTheseValues("id", fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
final String newName = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NEWNAME, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NEWNAME).value(newName).ignoreIfNull().notBlank().notExceedingLengthOf(50)
|
||||
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LENGTH, column)) {
|
||||
final String lengthStr = this.fromApiJsonHelper.extractStringNamed(LENGTH, column);
|
||||
if (this.fromApiJsonHelper.parameterExists(API_FIELD_LENGTH, column)) {
|
||||
final String lengthStr = this.fromApiJsonHelper.extractStringNamed(API_FIELD_LENGTH, column);
|
||||
if (StringUtils.isWhitespace(lengthStr) || !StringUtils.isNumeric(lengthStr) || StringUtils.isBlank(lengthStr)) {
|
||||
baseDataValidator.reset().parameter(LENGTH).failWithCode("not.greater.than.zero");
|
||||
baseDataValidator.reset().parameter(API_FIELD_LENGTH).failWithCode("not.greater.than.zero");
|
||||
} else {
|
||||
final Integer length = Integer.parseInt(lengthStr);
|
||||
baseDataValidator.reset().parameter(LENGTH).value(length).ignoreIfNull().notBlank().positiveAmount();
|
||||
baseDataValidator.reset().parameter(API_FIELD_LENGTH).value(length).ignoreIfNull().notBlank().positiveAmount();
|
||||
}
|
||||
}
|
||||
|
||||
final String code = this.fromApiJsonHelper.extractStringNamed(CODE, column);
|
||||
baseDataValidator.reset().parameter(CODE).value(code).ignoreIfNull().notBlank().notExceedingLengthOf(100)
|
||||
final String code = this.fromApiJsonHelper.extractStringNamed(API_FIELD_CODE, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_CODE).value(code).ignoreIfNull().notBlank().notExceedingLengthOf(100)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
final String newCode = this.fromApiJsonHelper.extractStringNamed(NEW_CODE, column);
|
||||
baseDataValidator.reset().parameter(NEW_CODE).value(newCode).ignoreIfNull().notBlank().notExceedingLengthOf(100)
|
||||
final String newCode = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NEWCODE, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NEWCODE).value(newCode).ignoreIfNull().notBlank().notExceedingLengthOf(100)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
if (StringUtils.isBlank(code) && StringUtils.isNotBlank(newCode)) {
|
||||
baseDataValidator.reset().parameter(CODE).value(code).cantBeBlankWhenParameterProvidedIs(NEW_CODE, newCode);
|
||||
baseDataValidator.reset().parameter(API_FIELD_CODE).value(code).cantBeBlankWhenParameterProvidedIs(API_FIELD_NEWCODE,
|
||||
newCode);
|
||||
}
|
||||
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(MANDATORY, column);
|
||||
baseDataValidator.reset().parameter(MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
|
||||
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(AFTER, column);
|
||||
baseDataValidator.reset().parameter(AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_AFTER, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(UNIQUE, column);
|
||||
baseDataValidator.reset().parameter(UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(INDEXED, column);
|
||||
baseDataValidator.reset().parameter(INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
final JsonArray addColumns = this.fromApiJsonHelper.extractJsonArrayNamed(ADD_COLUMNS, element);
|
||||
baseDataValidator.reset().parameter(ADD_COLUMNS).value(addColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
final JsonArray addColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_ADDCOLUMNS, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_ADDCOLUMNS).value(addColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
|
||||
if (addColumns != null) {
|
||||
for (final JsonElement column : addColumns) {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_ADD_COLUMNS);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, column);
|
||||
baseDataValidator.reset().parameter(NAME).value(name).notBlank().isNotOneOfTheseValues("id", fkColumnName)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
|
||||
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
|
||||
validateType(baseDataValidator, column);
|
||||
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(MANDATORY, column);
|
||||
baseDataValidator.reset().parameter(MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
|
||||
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(AFTER, column);
|
||||
baseDataValidator.reset().parameter(AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_AFTER, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(UNIQUE, column);
|
||||
baseDataValidator.reset().parameter(UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(INDEXED, column);
|
||||
baseDataValidator.reset().parameter(INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
|
||||
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
final JsonArray dropColumns = this.fromApiJsonHelper.extractJsonArrayNamed(DROP_COLUMNS, element);
|
||||
baseDataValidator.reset().parameter(DROP_COLUMNS).value(dropColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
final JsonArray dropColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_DROPCOLUMNS, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_DROPCOLUMNS).value(dropColumns).ignoreIfNull().jsonArrayNotEmpty();
|
||||
|
||||
if (dropColumns != null) {
|
||||
for (final JsonElement column : dropColumns) {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(NAME, column);
|
||||
baseDataValidator.reset().parameter(NAME).value(name).notBlank().isNotOneOfTheseValues("id", fkColumnName)
|
||||
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
|
||||
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
|
||||
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
|
||||
}
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void validateType(DataValidatorBuilder validator, final JsonElement column) {
|
||||
final String type = this.fromApiJsonHelper.extractStringNamed(API_FIELD_TYPE, column);
|
||||
validator.reset().parameter(API_FIELD_TYPE).value(type).notBlank().isOneOfTheseStringValues(SUPPORTED_COLUMN_TYPES);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
final Integer length = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(API_FIELD_LENGTH, column);
|
||||
JdbcJavaType jdbcType = mapApiTypeToJdbcType(type);
|
||||
validator = validator.reset().parameter(API_FIELD_LENGTH).value(length);
|
||||
if (jdbcType.hasPrecision(databaseTypeResolver.databaseType())) {
|
||||
if (jdbcType.isStringType() && length == null) {
|
||||
validator.failWithCode("must.be.provided.when.type.is.String");
|
||||
}
|
||||
validator.ignoreIfNull().positiveAmount();
|
||||
} // else, the precision is ignored
|
||||
|
||||
final String code = this.fromApiJsonHelper.extractStringNamed(API_FIELD_CODE, column);
|
||||
if (type.equalsIgnoreCase(API_FIELD_TYPE_DROPDOWN)) {
|
||||
if (code != null) {
|
||||
validator.reset().parameter(API_FIELD_CODE).value(code).notBlank().matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
|
||||
} else {
|
||||
validator.reset().parameter(API_FIELD_CODE).value(code).cantBeBlankWhenParameterProvidedIs(API_FIELD_TYPE, type);
|
||||
}
|
||||
} else {
|
||||
validator.reset().parameter(API_FIELD_CODE).value(code).mustBeBlankWhenParameterProvided(API_FIELD_TYPE, type);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JdbcJavaType mapApiTypeToJdbcType(@NotNull String apiType) {
|
||||
return switch (apiType.toLowerCase()) {
|
||||
case API_FIELD_TYPE_STRING -> VARCHAR;
|
||||
case API_FIELD_TYPE_NUMBER, API_FIELD_TYPE_DROPDOWN -> INTEGER;
|
||||
case API_FIELD_TYPE_BOOLEAN -> BOOLEAN;
|
||||
case API_FIELD_TYPE_DECIMAL -> DECIMAL;
|
||||
case API_FIELD_TYPE_DATE -> DATE;
|
||||
case API_FIELD_TYPE_DATETIME, API_FIELD_TYPE_TIMESTAMP -> DATETIME;
|
||||
case API_FIELD_TYPE_TEXT -> TEXT;
|
||||
default -> throw new PlatformDataIntegrityException("error.msg.datatable.column.type.invalid",
|
||||
"Column type " + apiType + " is not supported.");
|
||||
};
|
||||
}
|
||||
|
||||
private void validateEntitySubType(final DataValidatorBuilder baseDataValidator, final JsonElement element,
|
||||
final EntityTables entityTable) {
|
||||
if (entityTable == EntityTables.CLIENT) {
|
||||
String entitySubType = this.fromApiJsonHelper.extractStringNamed(API_PARAM_SUBTYPE, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_SUBTYPE).value(entitySubType).notBlank(); // Person or Entity
|
||||
}
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
|
||||
+34
-3
@@ -37,9 +37,6 @@ public final class DataTableApiConstant {
|
||||
public static final String CREATEDAT_FIELD_NAME = "created_at";
|
||||
public static final String UPDATEDAT_FIELD_NAME = "updated_at";
|
||||
|
||||
// Field Types
|
||||
public static final String DATETIME_FIELD_TYPE = "DateTime";
|
||||
|
||||
// associationParameters
|
||||
public static final String allAssociateParamName = "all";
|
||||
public static final String repaymentScheduleAssociateParamName = "repaymentSchedule";
|
||||
@@ -55,4 +52,38 @@ public final class DataTableApiConstant {
|
||||
public static final String meetingAssociateParamName = "meeting";
|
||||
public static final String emiAmountVariationsAssociateParamName = "emiAmountVariations";
|
||||
public static final String collectionAssociateParamName = "collection";
|
||||
|
||||
public static final String TABLE_COLUMN_CODE_MAPPINGS = "x_table_column_code_mappings";
|
||||
public static final String TABLE_REGISTERED_TABLE = "x_registered_table";
|
||||
public static final String TABLE_FIELD_ID = "id";
|
||||
|
||||
public static final String API_PARAM_COLUMNS = "columns";
|
||||
public static final String API_PARAM_CHANGECOLUMNS = "changeColumns";
|
||||
public static final String API_PARAM_ADDCOLUMNS = "addColumns";
|
||||
public static final String API_PARAM_DROPCOLUMNS = "dropColumns";
|
||||
public static final String API_PARAM_DATATABLE_NAME = "datatableName";
|
||||
public static final String API_PARAM_APPTABLE_NAME = "apptableName";
|
||||
public static final String API_PARAM_SUBTYPE = "entitySubType";
|
||||
public static final String API_PARAM_MULTIROW = "multiRow";
|
||||
|
||||
public static final String API_FIELD_NAME = "name";
|
||||
public static final String API_FIELD_NEWNAME = "newName";
|
||||
public static final String API_FIELD_TYPE = "type";
|
||||
public static final String API_FIELD_LENGTH = "length";
|
||||
public static final String API_FIELD_MANDATORY = "mandatory";
|
||||
public static final String API_FIELD_UNIQUE = "unique";
|
||||
public static final String API_FIELD_INDEXED = "indexed";
|
||||
public static final String API_FIELD_AFTER = "after";
|
||||
public static final String API_FIELD_CODE = "code";
|
||||
public static final String API_FIELD_NEWCODE = "newCode";
|
||||
|
||||
public static final String API_FIELD_TYPE_STRING = "string";
|
||||
public static final String API_FIELD_TYPE_NUMBER = "number";
|
||||
public static final String API_FIELD_TYPE_BOOLEAN = "boolean";
|
||||
public static final String API_FIELD_TYPE_DECIMAL = "decimal";
|
||||
public static final String API_FIELD_TYPE_DATE = "date";
|
||||
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_DROPDOWN = "dropdown";
|
||||
}
|
||||
|
||||
+81
-55
@@ -18,88 +18,114 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.dataqueries.data;
|
||||
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.ACTIVATE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.APPROVE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.CLOSE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.CREATE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.DISBURSE;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.REJECTED;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.WITHDRAWN;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.StatusEnum.WRITE_OFF;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum EntityTables {
|
||||
|
||||
CLIENT("m_client", ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.ACTIVATE.getCode(), StatusEnum.CLOSE.getCode()),
|
||||
"client_id"), LOAN("m_loan",
|
||||
ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.APPROVE.getCode(), StatusEnum.DISBURSE.getCode(),
|
||||
StatusEnum.WITHDRAWN.getCode(), StatusEnum.REJECTED.getCode(), StatusEnum.WRITE_OFF.getCode()),
|
||||
"loan_id"), GROUP("m_group",
|
||||
ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.ACTIVATE.getCode(), StatusEnum.CLOSE.getCode()),
|
||||
"group_id"), SAVING("m_savings_account",
|
||||
ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.APPROVE.getCode(),
|
||||
StatusEnum.ACTIVATE.getCode(), StatusEnum.WITHDRAWN.getCode(), StatusEnum.REJECTED.getCode(),
|
||||
StatusEnum.CLOSE.getCode()),
|
||||
"savings_account_id"), SAVINGS_TRANSACTION("m_savings_account_transaction", ImmutableList.of(),
|
||||
"savings_account_transcation_id"), OFFICE("m_office", ImmutableList.of(),
|
||||
"office_id"), PRODUCT_LOAN("m_product_loan", ImmutableList.of(),
|
||||
"product_loan_id"), SAVINGS_PRODUCT("m_savings_product", ImmutableList.of(),
|
||||
"savings_product_id"), SHARE_PRODUCT("m_share_product",
|
||||
ImmutableList.of(), "share_product_id");
|
||||
CLIENT("m_client", "client_id", "id", CREATE, ACTIVATE, CLOSE), //
|
||||
GROUP("m_group", "group_id", "id", CREATE, ACTIVATE, CLOSE), //
|
||||
CENTER("m_center", "m_group", "center_id", "id"), //
|
||||
OFFICE("m_office", "office_id", "id"), //
|
||||
LOAN_PRODUCT("m_product_loan", "product_loan_id", "id"), //
|
||||
LOAN("m_loan", "loan_id", "id", CREATE, APPROVE, DISBURSE, WITHDRAWN, REJECTED, WRITE_OFF), //
|
||||
SAVINGS_PRODUCT("m_savings_product", "savings_product_id", "id"), //
|
||||
SAVINGS("m_savings_account", "savings_account_id", "id", CREATE, APPROVE, ACTIVATE, WITHDRAWN, REJECTED, CLOSE), //
|
||||
SAVINGS_TRANSACTION("m_savings_account_transaction", "savings_transaction_id", "id"), //
|
||||
SHARE_PRODUCT("m_share_product", "share_product_id", "id"), //
|
||||
;
|
||||
|
||||
private static final Map<String, EntityTables> lookup = new HashMap<String, EntityTables>();
|
||||
public static final EntityTables[] VALUES = values();
|
||||
|
||||
static {
|
||||
for (EntityTables d : EntityTables.values()) {
|
||||
lookup.put(d.getName(), d);
|
||||
}
|
||||
}
|
||||
private static final List<String> ENTITY_NAMES = Arrays.stream(VALUES).map(EntityTables::getName).toList();
|
||||
|
||||
private static final Map<String, EntityTables> BY_ENTITY_NAME = Arrays.stream(VALUES)
|
||||
.collect(Collectors.toMap(EntityTables::getName, e -> e));
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ImmutableList<Integer> codes;
|
||||
private final String apptableName;
|
||||
|
||||
private final String foreignKeyColumnNameOnDatatable;
|
||||
private final String refColumn; // referenced column name on apptable
|
||||
|
||||
EntityTables(String name, ImmutableList<Integer> codes, String foreignKeyColumnNameOnDatatable) {
|
||||
private final ImmutableList<StatusEnum> checkStatuses;
|
||||
|
||||
EntityTables(String name, String apptableName, String foreignKeyColumnNameOnDatatable, String refColumn, StatusEnum... statuses) {
|
||||
this.name = name;
|
||||
this.codes = codes;
|
||||
this.apptableName = apptableName;
|
||||
this.foreignKeyColumnNameOnDatatable = foreignKeyColumnNameOnDatatable;
|
||||
this.refColumn = refColumn;
|
||||
this.checkStatuses = statuses == null ? ImmutableList.of() : ImmutableList.copyOf(statuses);
|
||||
}
|
||||
|
||||
public static List<String> getEntitiesList() {
|
||||
|
||||
List<String> data = new ArrayList<String>();
|
||||
|
||||
for (EntityTables entity : EntityTables.values()) {
|
||||
data.add(entity.name);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
public static ImmutableList<Integer> getStatus(String name) {
|
||||
if (lookup.get(name) != null) {
|
||||
return lookup.get(name).getCodes();
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public static EntityTables fromName(String name) {
|
||||
return name == null ? null : lookup.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
public ImmutableList<Integer> getCodes() {
|
||||
return this.codes;
|
||||
EntityTables(String name, String foreignKeyColumnNameOnDatatable, String refColumn, StatusEnum... statuses) {
|
||||
this(name, name, foreignKeyColumnNameOnDatatable, refColumn, statuses);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getApptableName() {
|
||||
return apptableName;
|
||||
}
|
||||
|
||||
public String getForeignKeyColumnNameOnDatatable() {
|
||||
return this.foreignKeyColumnNameOnDatatable;
|
||||
}
|
||||
|
||||
public static String getForeignKeyColumnNameOnDatatable(String name) {
|
||||
return lookup.get(name).foreignKeyColumnNameOnDatatable;
|
||||
public String getRefColumn() {
|
||||
return refColumn;
|
||||
}
|
||||
|
||||
public List<StatusEnum> getCheckStatuses() {
|
||||
return checkStatuses;
|
||||
}
|
||||
|
||||
public boolean hasCheck() {
|
||||
return checkStatuses != null && !checkStatuses.isEmpty();
|
||||
}
|
||||
|
||||
public static List<String> getEntityNames() {
|
||||
return ENTITY_NAMES;
|
||||
}
|
||||
|
||||
public static EntityTables fromEntityName(String name) {
|
||||
return name == null ? null : BY_ENTITY_NAME.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
public static String getForeignKeyColumnNameOnDatatable(String name) {
|
||||
EntityTables entityTable = fromEntityName(name);
|
||||
return entityTable == null ? null : entityTable.getForeignKeyColumnNameOnDatatable();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<StatusEnum> getCheckStatuses(String name) {
|
||||
EntityTables entityTable = fromEntityName(name);
|
||||
return entityTable == null ? List.of() : entityTable.getCheckStatuses();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Integer> getCheckStatusCodes(String name) {
|
||||
return getCheckStatuses(name).stream().map(StatusEnum::getCode).toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<EntityTables> getFiltered(Predicate<EntityTables> filter) {
|
||||
return Arrays.stream(VALUES).filter(filter).toList();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.infrastructure.dataqueries.data;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
|
||||
/**
|
||||
* Immutable data object for generic resultset data.
|
||||
@@ -41,16 +42,13 @@ public final class GenericResultsetData {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public String getColTypeOfColumnNamed(final String columnName) {
|
||||
|
||||
String colType = null;
|
||||
public JdbcJavaType getColTypeOfColumnNamed(final String columnName) {
|
||||
for (final ResultsetColumnHeaderData columnHeader : this.columnHeaders) {
|
||||
if (columnHeader.isNamed(columnName)) {
|
||||
colType = columnHeader.getColumnType();
|
||||
return columnHeader.getColumnType();
|
||||
}
|
||||
}
|
||||
|
||||
return colType;
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasNoEntries() {
|
||||
|
||||
+9
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.infrastructure.dataqueries.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when datatable resources are not found.
|
||||
@@ -32,4 +33,12 @@ public class DatatableNotFoundException extends AbstractPlatformResourceNotFound
|
||||
public DatatableNotFoundException(final String datatable) {
|
||||
super("error.msg.datatable.not.found", "Datatable not found.", datatable);
|
||||
}
|
||||
|
||||
public DatatableNotFoundException(final EntityTables entityTable, final Long id) {
|
||||
this(entityTable.getName(), id);
|
||||
}
|
||||
|
||||
public DatatableNotFoundException(final EntityTables entityTable) {
|
||||
this(entityTable.getName());
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.dataqueries.service;
|
||||
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_DATATABLE_NAME;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -41,7 +43,6 @@ import org.springframework.stereotype.Component;
|
||||
public final class EntityDatatableChecksDataValidator {
|
||||
|
||||
public static final String ENTITY = "entity";
|
||||
public static final String DATATABLE_NAME = "datatableName";
|
||||
public static final String STATUS = "status";
|
||||
public static final String SYSTEM_DEFINED = "systemDefined";
|
||||
public static final String PRODUCT_ID = "productId";
|
||||
@@ -50,7 +51,7 @@ public final class EntityDatatableChecksDataValidator {
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
private static final Set<String> SUPPORTED_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList(ENTITY, DATATABLE_NAME, STATUS, SYSTEM_DEFINED, PRODUCT_ID));
|
||||
Arrays.asList(ENTITY, API_PARAM_DATATABLE_NAME, STATUS, SYSTEM_DEFINED, PRODUCT_ID));
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
@@ -74,15 +75,15 @@ public final class EntityDatatableChecksDataValidator {
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String entity = this.fromApiJsonHelper.extractStringNamed(ENTITY, element);
|
||||
baseDataValidator.reset().parameter(ENTITY).value(entity).notBlank().isOneOfTheseStringValues(EntityTables.getEntitiesList());
|
||||
baseDataValidator.reset().parameter(ENTITY).value(entity).notBlank().isOneOfTheseStringValues(EntityTables.getEntityNames());
|
||||
|
||||
final Integer status = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(STATUS, element);
|
||||
final Object[] entityTablesStatuses = EntityTables.getStatus(entity).toArray();
|
||||
final Object[] entityTablesStatuses = EntityTables.getCheckStatusCodes(entity).toArray();
|
||||
|
||||
baseDataValidator.reset().parameter(STATUS).value(status).isOneOfTheseValues(entityTablesStatuses);
|
||||
|
||||
final String datatableName = this.fromApiJsonHelper.extractStringNamed(DATATABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(DATATABLE_NAME).value(datatableName).notBlank();
|
||||
final String datatableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_DATATABLE_NAME, element);
|
||||
baseDataValidator.reset().parameter(API_PARAM_DATATABLE_NAME).value(datatableName).notBlank();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SYSTEM_DEFINED, element)) {
|
||||
final String systemDefined = this.fromApiJsonHelper.extractStringNamed(SYSTEM_DEFINED, element);
|
||||
|
||||
+15
-17
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.dataqueries.service;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,6 +29,7 @@ import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableCheckStatusData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableChecksData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
@@ -52,6 +52,7 @@ import org.springframework.stereotype.Service;
|
||||
public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatatableChecksReadService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
protected final DatabaseTypeResolver databaseTypeResolver;
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
private final RegisterDataTableMapper registerDataTableMapper;
|
||||
private final EntityDataTableChecksMapper entityDataTableChecksMapper;
|
||||
@@ -62,14 +63,14 @@ public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatat
|
||||
private final PaginationHelper paginationHelper;
|
||||
|
||||
@Autowired
|
||||
public EntityDatatableChecksReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate,
|
||||
final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
public EntityDatatableChecksReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseTypeResolver databaseTypeResolver,
|
||||
DatabaseSpecificSQLGenerator sqlGenerator, final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final SavingsProductReadPlatformService savingsProductReadPlatformService,
|
||||
final EntityDatatableChecksRepository entityDatatableChecksRepository,
|
||||
final ReadWriteNonCoreDataService readWriteNonCoreDataService, DatabaseSpecificSQLGenerator sqlGenerator,
|
||||
PaginationHelper paginationHelper) {
|
||||
final ReadWriteNonCoreDataService readWriteNonCoreDataService, PaginationHelper paginationHelper) {
|
||||
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.databaseTypeResolver = databaseTypeResolver;
|
||||
this.sqlGenerator = sqlGenerator;
|
||||
this.registerDataTableMapper = new RegisterDataTableMapper();
|
||||
this.entityDataTableChecksMapper = new EntityDataTableChecksMapper();
|
||||
@@ -141,28 +142,25 @@ public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatat
|
||||
|
||||
@Override
|
||||
public EntityDataTableChecksTemplateData retrieveTemplate() {
|
||||
|
||||
List<DatatableChecksData> dataTables = getDataTables();
|
||||
List<String> entities = EntityTables.getEntitiesList();
|
||||
List<DatatableCheckStatusData> statusClient = getStatusList(EntityTables.getStatus("m_client"));
|
||||
List<DatatableCheckStatusData> statusLoan = getStatusList(EntityTables.getStatus("m_loan"));
|
||||
List<DatatableCheckStatusData> statusGroup = getStatusList(EntityTables.getStatus("m_group"));
|
||||
List<DatatableCheckStatusData> statusSavings = getStatusList(EntityTables.getStatus("m_savings_account"));
|
||||
List<String> entities = EntityTables.getEntityNames();
|
||||
List<DatatableCheckStatusData> clientStatuses = getStatusList(EntityTables.CLIENT.getCheckStatuses());
|
||||
List<DatatableCheckStatusData> loanStatuses = getStatusList(EntityTables.LOAN.getCheckStatuses());
|
||||
List<DatatableCheckStatusData> groupstatuses = getStatusList(EntityTables.GROUP.getCheckStatuses());
|
||||
List<DatatableCheckStatusData> savingsStatuses = getStatusList(EntityTables.SAVINGS.getCheckStatuses());
|
||||
|
||||
Collection<LoanProductData> loanProductDatas = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(true);
|
||||
Collection<SavingsProductData> savingsProductDatas = this.savingsProductReadPlatformService.retrieveAllForLookup();
|
||||
|
||||
return new EntityDataTableChecksTemplateData(entities, statusClient, statusGroup, statusSavings, statusLoan, dataTables,
|
||||
return new EntityDataTableChecksTemplateData(entities, clientStatuses, groupstatuses, savingsStatuses, loanStatuses, dataTables,
|
||||
loanProductDatas, savingsProductDatas);
|
||||
|
||||
}
|
||||
|
||||
private List<DatatableCheckStatusData> getStatusList(ImmutableList<Integer> statuses) {
|
||||
private List<DatatableCheckStatusData> getStatusList(List<StatusEnum> statuses) {
|
||||
List<DatatableCheckStatusData> ret = new ArrayList<>();
|
||||
if (statuses != null) {
|
||||
for (Integer status : statuses) {
|
||||
StatusEnum statusEnum = StatusEnum.fromInt(status);
|
||||
ret.add(new DatatableCheckStatusData(statusEnum.name(), statusEnum.getCode()));
|
||||
for (StatusEnum status : statuses) {
|
||||
ret.add(new DatatableCheckStatusData(status.name(), status.getCode()));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
+3
-4
@@ -69,7 +69,6 @@ public class EntityDatatableChecksWritePlatformServiceImpl implements EntityData
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult createCheck(final JsonCommand command) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
@@ -105,11 +104,11 @@ public class EntityDatatableChecksWritePlatformServiceImpl implements EntityData
|
||||
throw new EntityDatatableCheckAlreadyExistsException(entity, status, datatableName);
|
||||
}
|
||||
} else {
|
||||
EntityTables entityTable = EntityTables.fromName(entity);
|
||||
if (EntityTables.LOAN == entityTable) {
|
||||
EntityTables entityTable = EntityTables.fromEntityName(entity);
|
||||
if (entityTable == EntityTables.LOAN) {
|
||||
// if invalid loan product id, throws exception
|
||||
this.loanProductReadPlatformService.retrieveLoanProduct(productId);
|
||||
} else if (EntityTables.SAVING == entityTable) {
|
||||
} else if (entityTable == EntityTables.SAVINGS) {
|
||||
// if invalid savings product id, throws exception
|
||||
this.savingsProductReadPlatformService.retrieveOne(productId);
|
||||
} else {
|
||||
|
||||
+7
-4
@@ -21,20 +21,23 @@ package org.apache.fineract.infrastructure.dataqueries.service;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
|
||||
|
||||
public interface GenericDataService {
|
||||
|
||||
GenericResultsetData fillGenericResultSet(String sql);
|
||||
|
||||
List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String tableName);
|
||||
|
||||
List<ResultsetRowData> fillResultsetRowData(String sql, List<ResultsetColumnHeaderData> columnHeaders);
|
||||
|
||||
String generateJsonFromGenericResultsetData(GenericResultsetData grs);
|
||||
|
||||
String replace(String str, String pattern, String replace);
|
||||
|
||||
String wrapSQL(String sql);
|
||||
|
||||
List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String datatable);
|
||||
boolean isExplicitlyUnique(String tableName, String columnName);
|
||||
|
||||
boolean isExplicitlyUnique(String datatable, String columnName);
|
||||
|
||||
boolean isExplicitlyIndexed(String datatable, String columnName);
|
||||
boolean isExplicitlyIndexed(String tableName, String columnName);
|
||||
}
|
||||
|
||||
+129
-116
@@ -19,23 +19,35 @@
|
||||
package org.apache.fineract.infrastructure.dataqueries.service;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATE;
|
||||
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATETIME;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData.DisplayType.CODELOOKUP;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData.DisplayType.DECIMAL;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData.DisplayType.INTEGER;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData.DisplayType.TIME;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||
import org.apache.fineract.infrastructure.core.service.database.IndexDetail;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSource;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnValueData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
@@ -51,6 +63,7 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
private final RoutingDataSource dataSource;
|
||||
private final DatabaseIndependentQueryService databaseIndependentQueryService;
|
||||
private final DatatableKeywordGenerator datatableKeywordGenerator;
|
||||
private final DatabaseTypeResolver databaseTypeResolver;
|
||||
|
||||
@Override
|
||||
public GenericResultsetData fillGenericResultSet(final String sql) {
|
||||
@@ -58,38 +71,18 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql); // NOSONAR
|
||||
|
||||
final List<ResultsetColumnHeaderData> columnHeaders = new ArrayList<>();
|
||||
final List<ResultsetRowData> resultsetDataRows = new ArrayList<>();
|
||||
|
||||
final SqlRowSetMetaData rsmd = rs.getMetaData();
|
||||
|
||||
for (int i = 0; i < rsmd.getColumnCount(); i++) {
|
||||
|
||||
final String columnName = rsmd.getColumnName(i + 1);
|
||||
final String columnType = rsmd.getColumnTypeName(i + 1);
|
||||
|
||||
final ResultsetColumnHeaderData columnHeader = ResultsetColumnHeaderData.basic(columnName, columnType);
|
||||
final ResultsetColumnHeaderData columnHeader = ResultsetColumnHeaderData.basic(columnName, columnType,
|
||||
databaseTypeResolver.databaseType());
|
||||
columnHeaders.add(columnHeader);
|
||||
}
|
||||
|
||||
while (rs.next()) {
|
||||
final List<Object> columnValues = new ArrayList<>();
|
||||
for (int i = 0; i < rsmd.getColumnCount(); i++) {
|
||||
final String columnName = rsmd.getColumnName(i + 1);
|
||||
final String colType = columnHeaders.get(i).getColumnType();
|
||||
if ("DATE".equalsIgnoreCase(colType)) {
|
||||
java.sql.Date tmpDate = (java.sql.Date) rs.getObject(columnName);
|
||||
columnValues.add(tmpDate != null ? tmpDate.toLocalDate() : null);
|
||||
} else if ("TIMESTAMP WITHOUT TIME ZONE".equalsIgnoreCase(colType) // PostgreSQL
|
||||
|| "DATETIME".equalsIgnoreCase(colType) || "TIMESTAMP".equalsIgnoreCase(colType)) {
|
||||
Timestamp tmpDate = (Timestamp) rs.getObject(columnName);
|
||||
columnValues.add(tmpDate != null ? tmpDate.toLocalDateTime() : null);
|
||||
} else {
|
||||
columnValues.add(rs.getObject(columnName));
|
||||
}
|
||||
}
|
||||
final ResultsetRowData resultsetDataRow = ResultsetRowData.create(columnValues);
|
||||
resultsetDataRows.add(resultsetDataRow);
|
||||
}
|
||||
final List<ResultsetRowData> resultsetDataRows = fillResultsetRowData(rs, columnHeaders);
|
||||
|
||||
return new GenericResultsetData(columnHeaders, resultsetDataRows);
|
||||
} catch (DataAccessException e) {
|
||||
@@ -98,6 +91,80 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String tableName) {
|
||||
final SqlRowSet columnDefinitions = getTableMetaData(tableName);
|
||||
final List<IndexDetail> indexDefinitions = getDatatableIndexData(tableName);
|
||||
|
||||
DatabaseType dialect = databaseTypeResolver.databaseType();
|
||||
final List<ResultsetColumnHeaderData> columnHeaders = new ArrayList<>();
|
||||
|
||||
columnDefinitions.beforeFirst();
|
||||
while (columnDefinitions.next()) {
|
||||
final String columnName = columnDefinitions.getString(1);
|
||||
final String isNullable = columnDefinitions.getString(2);
|
||||
final String isPrimaryKey = columnDefinitions.getString(5);
|
||||
final String columnType = columnDefinitions.getString(3);
|
||||
final Long columnLength = columnDefinitions.getLong(4);
|
||||
|
||||
final boolean columnNullable = "YES".equalsIgnoreCase(isNullable) || "TRUE".equalsIgnoreCase(isNullable);
|
||||
final boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey) || "TRUE".equalsIgnoreCase(isPrimaryKey);
|
||||
|
||||
// primary keys are automatically unique
|
||||
final boolean columnIsUnique = columnIsPrimaryKey || isExplicitlyUnique(tableName, columnName, indexDefinitions);
|
||||
|
||||
// primary keys and unique constrained columns are automatically indexed
|
||||
final boolean columnIsIndexed = columnIsPrimaryKey || columnIsUnique
|
||||
|| isExplicitlyIndexed(tableName, columnName, indexDefinitions);
|
||||
JdbcJavaType jdbcType = JdbcJavaType.getByTypeName(dialect, columnType);
|
||||
|
||||
List<ResultsetColumnValueData> columnValues = new ArrayList<>();
|
||||
String codeName = null;
|
||||
final int codePosition = columnName.indexOf("_cd");
|
||||
if (codePosition > 0 && jdbcType != null && (jdbcType.isVarcharType() || jdbcType.isIntegerType())) {
|
||||
codeName = columnName.substring(0, codePosition);
|
||||
columnValues = retrieveCodeValues(codeName);
|
||||
}
|
||||
|
||||
columnHeaders.add(ResultsetColumnHeaderData.detailed(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey,
|
||||
columnValues, codeName, columnIsUnique, columnIsIndexed, dialect));
|
||||
}
|
||||
|
||||
return columnHeaders;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<ResultsetRowData> fillResultsetRowData(final String sql, List<ResultsetColumnHeaderData> columnHeaders) {
|
||||
final SqlRowSet rs = jdbcTemplate.queryForRowSet(sql); // NOSONAR
|
||||
return fillResultsetRowData(rs, columnHeaders);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<ResultsetRowData> fillResultsetRowData(SqlRowSet rs, List<ResultsetColumnHeaderData> columnHeaders) {
|
||||
final SqlRowSetMetaData rsmd = rs.getMetaData();
|
||||
final List<ResultsetRowData> resultsetDataRows = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
final List<Object> columnValues = new ArrayList<>();
|
||||
for (int i = 0; i < rsmd.getColumnCount(); i++) {
|
||||
final String columnName = rsmd.getColumnName(i + 1);
|
||||
final JdbcJavaType colType = columnHeaders.get(i).getColumnType();
|
||||
if (colType == DATE) {
|
||||
Date tmpDate = (Date) rs.getObject(columnName);
|
||||
columnValues.add(tmpDate == null ? null : tmpDate.toLocalDate());
|
||||
} else if (colType == DATETIME) {
|
||||
Object tmpDate = rs.getObject(columnName);
|
||||
columnValues.add(
|
||||
tmpDate == null ? null : (tmpDate instanceof Timestamp ? ((Timestamp) tmpDate).toLocalDateTime() : tmpDate));
|
||||
} else {
|
||||
columnValues.add(rs.getObject(columnName));
|
||||
}
|
||||
}
|
||||
resultsetDataRows.add(ResultsetRowData.create(columnValues));
|
||||
}
|
||||
return resultsetDataRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String replace(final String str, final String pattern, final String replace) {
|
||||
// JPW - this replace may / may not be any better or quicker than the
|
||||
@@ -129,7 +196,6 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
|
||||
@Override
|
||||
public String generateJsonFromGenericResultsetData(final GenericResultsetData grs) {
|
||||
|
||||
final StringBuilder writer = new StringBuilder();
|
||||
|
||||
writer.append("[");
|
||||
@@ -141,7 +207,7 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
Integer rSize;
|
||||
final String doubleQuote = "\"";
|
||||
final String slashDoubleQuote = "\\\"";
|
||||
String currColType;
|
||||
ResultsetColumnHeaderData.DisplayType colDisplayType;
|
||||
Object currVal;
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
@@ -150,39 +216,31 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
row = data.get(i).getRow();
|
||||
rSize = row.size();
|
||||
for (int j = 0; j < rSize; j++) {
|
||||
|
||||
writer.append(doubleQuote + columnHeaders.get(j).getColumnName() + doubleQuote + ": ");
|
||||
currColType = columnHeaders.get(j).getColumnDisplayType();
|
||||
final String colType = columnHeaders.get(j).getColumnType();
|
||||
if (currColType == null && colType.equalsIgnoreCase("INT")) {
|
||||
currColType = "INTEGER";
|
||||
}
|
||||
if (currColType == null && colType.equalsIgnoreCase("VARCHAR")) {
|
||||
currColType = "VARCHAR";
|
||||
}
|
||||
if (currColType == null && colType.equalsIgnoreCase("DATE")) {
|
||||
currColType = "DATE";
|
||||
}
|
||||
if (currColType == null && colType.equalsIgnoreCase("DATETIME")) {
|
||||
currColType = "DATETIME";
|
||||
ResultsetColumnHeaderData columnHeader = columnHeaders.get(j);
|
||||
writer.append(doubleQuote + columnHeader.getColumnName() + doubleQuote + ": ");
|
||||
colDisplayType = columnHeader.getColumnDisplayType();
|
||||
final JdbcJavaType colType = columnHeader.getColumnType();
|
||||
if (colDisplayType == null) {
|
||||
colDisplayType = ResultsetColumnHeaderData.calcColumnDisplayType(colType);
|
||||
}
|
||||
currVal = row.get(j);
|
||||
if (currVal != null && currColType != null) {
|
||||
if (currColType.equalsIgnoreCase("DECIMAL") || currColType.equalsIgnoreCase("INTEGER")
|
||||
|| currColType.equalsIgnoreCase("CODELOOKUP")) {
|
||||
if (currVal != null && colDisplayType != null) {
|
||||
if (colDisplayType == ResultsetColumnHeaderData.DisplayType.DATE) {
|
||||
final LocalDate localDate = (LocalDate) currVal;
|
||||
writer.append(format("[%d,%d,%d]", localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()));
|
||||
} else if (colDisplayType == ResultsetColumnHeaderData.DisplayType.DATETIME) {
|
||||
final LocalDateTime localDateTime = (LocalDateTime) currVal;
|
||||
writer.append(format("[%d,%d,%d,%d,%d,%d,%d]", localDateTime.getYear(), localDateTime.getMonthValue(),
|
||||
localDateTime.getDayOfMonth(), localDateTime.getHour(), localDateTime.getMinute(),
|
||||
localDateTime.getSecond(), localDateTime.getNano()));
|
||||
} else if (colDisplayType == TIME) {
|
||||
final LocalTime localTime = (LocalTime) currVal;
|
||||
writer.append(format("[%d,%d,%d,%d]", localTime.getHour(), localTime.getMinute(), localTime.getSecond(),
|
||||
localTime.getNano()));
|
||||
} else if (colDisplayType == DECIMAL || colDisplayType == INTEGER || colDisplayType == CODELOOKUP) {
|
||||
writer.append(currVal);
|
||||
} else {
|
||||
if (currColType.equalsIgnoreCase("DATE")) {
|
||||
final LocalDate localDate = (LocalDate) currVal;
|
||||
writer.append(format("[%d,%d,%d]", localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()));
|
||||
} else if (currColType.equalsIgnoreCase("DATETIME")) {
|
||||
final LocalDateTime localDateTime = (LocalDateTime) currVal;
|
||||
writer.append(format("[%d,%d,%d,%d,%d,%d,%d]", localDateTime.getYear(), localDateTime.getMonthValue(),
|
||||
localDateTime.getDayOfMonth(), localDateTime.getHour(), localDateTime.getMinute(),
|
||||
localDateTime.getSecond(), localDateTime.getNano()));
|
||||
} else {
|
||||
writer.append(doubleQuote + replace(String.valueOf(currVal), doubleQuote, slashDoubleQuote) + doubleQuote);
|
||||
}
|
||||
writer.append(doubleQuote + replace(String.valueOf(currVal), doubleQuote, slashDoubleQuote) + doubleQuote);
|
||||
}
|
||||
} else {
|
||||
writer.append("null");
|
||||
@@ -201,69 +259,26 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
|
||||
writer.append("\n]");
|
||||
return writer.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String datatable) {
|
||||
final SqlRowSet columnDefinitions = getDatatableMetaData(datatable);
|
||||
final List<IndexDetail> indexDefinitions = getDatatableIndexData(datatable);
|
||||
|
||||
final List<ResultsetColumnHeaderData> columnHeaders = new ArrayList<>();
|
||||
|
||||
columnDefinitions.beforeFirst();
|
||||
while (columnDefinitions.next()) {
|
||||
final String columnName = columnDefinitions.getString(1);
|
||||
final String isNullable = columnDefinitions.getString(2);
|
||||
final String isPrimaryKey = columnDefinitions.getString(5);
|
||||
final String columnType = columnDefinitions.getString(3);
|
||||
final Long columnLength = columnDefinitions.getLong(4);
|
||||
|
||||
final boolean columnNullable = "YES".equalsIgnoreCase(isNullable) || "TRUE".equalsIgnoreCase(isNullable);
|
||||
final boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey) || "TRUE".equalsIgnoreCase(isPrimaryKey);
|
||||
|
||||
// primary keys are automatically unique
|
||||
final boolean columnIsUnique = columnIsPrimaryKey || isExplicitlyUnique(datatable, columnName, indexDefinitions);
|
||||
|
||||
// primary keys and unique constrained columns are automatically indexed
|
||||
final boolean columnIsIndexed = columnIsPrimaryKey || columnIsUnique
|
||||
|| isExplicitlyIndexed(datatable, columnName, indexDefinitions);
|
||||
|
||||
List<ResultsetColumnValueData> columnValues = new ArrayList<>();
|
||||
String codeName = null;
|
||||
final int codePosition = columnName.indexOf("_cd");
|
||||
if ("varchar".equalsIgnoreCase(columnType) || "int".equalsIgnoreCase(columnType) || "integer".equalsIgnoreCase(columnType)) {
|
||||
if (codePosition > 0) {
|
||||
codeName = columnName.substring(0, codePosition);
|
||||
columnValues = retrieveColumnValues(codeName);
|
||||
}
|
||||
}
|
||||
|
||||
columnHeaders.add(ResultsetColumnHeaderData.detailed(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey,
|
||||
columnValues, codeName, columnIsUnique, columnIsIndexed));
|
||||
}
|
||||
|
||||
return columnHeaders;
|
||||
}
|
||||
|
||||
private boolean isExplicitlyUnique(String datatable, String columnName, List<IndexDetail> indexDefinitions) {
|
||||
String keyNameToCheck = datatableKeywordGenerator.generateUniqueKeyName(datatable, columnName);
|
||||
private boolean isExplicitlyUnique(String tableName, String columnName, List<IndexDetail> indexDefinitions) {
|
||||
String keyNameToCheck = datatableKeywordGenerator.generateUniqueKeyName(tableName, columnName);
|
||||
return checkKeyPresent(keyNameToCheck, indexDefinitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicitlyUnique(String datatable, String columnName) {
|
||||
return isExplicitlyUnique(datatable, columnName, getDatatableIndexData(datatable));
|
||||
public boolean isExplicitlyUnique(String tableName, String columnName) {
|
||||
return isExplicitlyUnique(tableName, columnName, getDatatableIndexData(tableName));
|
||||
}
|
||||
|
||||
private boolean isExplicitlyIndexed(String datatable, String columnName, List<IndexDetail> indexDefinitions) {
|
||||
String keyNameToCheck = datatableKeywordGenerator.generateIndexName(datatable, columnName);
|
||||
private boolean isExplicitlyIndexed(String tableName, String columnName, List<IndexDetail> indexDefinitions) {
|
||||
String keyNameToCheck = datatableKeywordGenerator.generateIndexName(tableName, columnName);
|
||||
return checkKeyPresent(keyNameToCheck, indexDefinitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExplicitlyIndexed(String datatable, String columnName) {
|
||||
return isExplicitlyIndexed(datatable, columnName, getDatatableIndexData(datatable));
|
||||
public boolean isExplicitlyIndexed(String tableName, String columnName) {
|
||||
return isExplicitlyIndexed(tableName, columnName, getDatatableIndexData(tableName));
|
||||
}
|
||||
|
||||
private boolean checkKeyPresent(String keyNameToCheck, List<IndexDetail> indexDefinitions) {
|
||||
@@ -276,25 +291,23 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
}
|
||||
|
||||
@SuppressWarnings("AvoidHidingCauseException")
|
||||
private List<IndexDetail> getDatatableIndexData(String datatable) {
|
||||
private List<IndexDetail> getDatatableIndexData(String tableName) {
|
||||
try {
|
||||
return databaseIndependentQueryService.getTableIndexes(dataSource, datatable);
|
||||
return databaseIndependentQueryService.getTableIndexes(dataSource, tableName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new DatatableNotFoundException(datatable);
|
||||
throw new DatatableNotFoundException(tableName);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Candidate for using caching there to get allowed 'column values' from code/codevalue tables
|
||||
*/
|
||||
private List<ResultsetColumnValueData> retrieveColumnValues(final String codeName) {
|
||||
private List<ResultsetColumnValueData> retrieveCodeValues(final String codeName) {
|
||||
final String sql = "select v.id, v.code_score, v.code_value from m_code m join m_code_value v on v.code_id = m.id where m.code_name = ? order by v.order_position, v.id";
|
||||
final SqlRowSet rsValues = this.jdbcTemplate.queryForRowSet(sql, codeName); // NOSONAR
|
||||
|
||||
final List<ResultsetColumnValueData> columnValues = new ArrayList<>();
|
||||
|
||||
final String sql = "select v.id, v.code_score, v.code_value from m_code m join m_code_value v on v.code_id = m.id where m.code_name = ? order by v.order_position, v.id";
|
||||
|
||||
final SqlRowSet rsValues = this.jdbcTemplate.queryForRowSet(sql, new Object[] { codeName }); // NOSONAR
|
||||
|
||||
rsValues.beforeFirst();
|
||||
while (rsValues.next()) {
|
||||
final Integer id = rsValues.getInt("id");
|
||||
@@ -308,11 +321,11 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
}
|
||||
|
||||
@SuppressWarnings("AvoidHidingCauseException")
|
||||
private SqlRowSet getDatatableMetaData(final String datatable) {
|
||||
private SqlRowSet getTableMetaData(final String tableName) {
|
||||
try {
|
||||
return databaseIndependentQueryService.getTableColumns(dataSource, datatable);
|
||||
return databaseIndependentQueryService.getTableColumns(dataSource, tableName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new DatatableNotFoundException(datatable);
|
||||
throw new DatatableNotFoundException(tableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ReportParameterData;
|
||||
@@ -230,7 +231,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
table.completeRow();
|
||||
|
||||
Integer rSize;
|
||||
String currColType;
|
||||
JdbcJavaType currColType;
|
||||
String currVal;
|
||||
log.debug("NO. of Rows: {}", data.size());
|
||||
for (ResultsetRowData element : data) {
|
||||
@@ -240,9 +241,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
currColType = columnHeaders.get(j).getColumnType();
|
||||
currVal = (String) row.get(j);
|
||||
if (currVal != null) {
|
||||
if (currColType.equals("DECIMAL") || currColType.equals("DOUBLE") || currColType.equals("BIGINT")
|
||||
|| currColType.equals("SMALLINT") || currColType.equals("INT")) {
|
||||
|
||||
if (currColType.isNumericType()) {
|
||||
table.addCell(currVal.toString());
|
||||
} else {
|
||||
table.addCell(currVal.toString());
|
||||
|
||||
+365
-685
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -22,7 +22,7 @@ import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
|
||||
public class MoneyData {
|
||||
|
||||
+1
-1
@@ -48,6 +48,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.interoperation.data.InteropAccountData;
|
||||
@@ -76,7 +77,6 @@ import org.apache.fineract.interoperation.exception.InteropTransferAlreadyCommit
|
||||
import org.apache.fineract.interoperation.exception.InteropTransferAlreadyOnHoldException;
|
||||
import org.apache.fineract.interoperation.exception.InteropTransferMissingException;
|
||||
import org.apache.fineract.interoperation.serialization.InteropDataValidator;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
|
||||
+1
-1
@@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
||||
@@ -63,7 +64,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionRecoveryPaymentPostBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionRecoveryPaymentPreBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidDetail;
|
||||
|
||||
+2
-2
@@ -1230,7 +1230,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
}
|
||||
|
||||
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService
|
||||
.retrieveTemplates(StatusEnum.CREATE.getCode().longValue(), EntityTables.SAVING.getName(), productId);
|
||||
.retrieveTemplates(StatusEnum.CREATE.getCode().longValue(), EntityTables.SAVINGS.getName(), productId);
|
||||
template.setDatatables(datatableTemplates);
|
||||
|
||||
return template;
|
||||
@@ -1324,7 +1324,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount,");
|
||||
sqlBuilder.append(" tr.release_id_of_hold_amount as releaseTransactionId,");
|
||||
sqlBuilder.append(" tr.reason_for_block as reasonForBlock,");
|
||||
sqlBuilder.append(" tr.submitted_on_date as submittedOnDate,");
|
||||
sqlBuilder.append("tr.submitted_on_date as submittedOnDate,");
|
||||
sqlBuilder.append(" au.username as submittedByUsername, ");
|
||||
sqlBuilder.append(" nt.note as transactionNote, ");
|
||||
sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,");
|
||||
|
||||
+6
-10
@@ -207,8 +207,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
final Map<String, Object> changes = account.activate(user, command, DateUtils.getBusinessLocalDate());
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.ACTIVATE.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(), account.productId());
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.ACTIVATE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), account.productId());
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
final Locale locale = command.extractLocale();
|
||||
@@ -924,8 +924,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
throw new SavingsAccountClosingNotAllowedException("linked", defaultUserMessage, savingsId);
|
||||
}
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.CLOSE.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(), account.productId());
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.CLOSE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), account.productId());
|
||||
|
||||
final boolean isWithdrawBalance = command.booleanPrimitiveValueOfParameterNamed(withdrawBalanceParamName);
|
||||
|
||||
@@ -1517,9 +1517,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME);
|
||||
|
||||
/***
|
||||
* Only recurring fees are allowed to inactivate
|
||||
*/
|
||||
// Only recurring fees are allowed to inactivate
|
||||
if (!savingsAccountCharge.isRecurringFee()) {
|
||||
baseDataValidator.reset().parameter(null).value(savingsAccountCharge.getId())
|
||||
.failWithCodeNoParameterAddedToErrorCode("charge.inactivation.allowed.only.for.recurring.charges");
|
||||
@@ -1547,9 +1545,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
chargePayments.add(transaction);
|
||||
}
|
||||
}
|
||||
/***
|
||||
* Reverse the excess payments of charge transactions
|
||||
*/
|
||||
// Reverse the excess payments of charge transactions
|
||||
SavingsAccountTransaction lastChargePayment = getLastChargePayment(chargePayments);
|
||||
this.undoTransaction(savingsAccountCharge.savingsAccount().getId(), lastChargePayment.getId(), false);
|
||||
updatedCharge = account.getUpdatedChargeDetails(savingsAccountCharge);
|
||||
|
||||
+10
-9
@@ -257,11 +257,12 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
final Long savingsId = account.getId();
|
||||
if (command.parameterExists(SavingsApiConstants.datatables)) {
|
||||
this.entityDatatableChecksWritePlatformService.saveDatatables(StatusEnum.CREATE.getCode().longValue(),
|
||||
EntityTables.SAVING.getName(), savingsId, account.productId(),
|
||||
EntityTables.SAVINGS.getName(), savingsId, account.productId(),
|
||||
command.arrayOfParameterNamed(SavingsApiConstants.datatables));
|
||||
}
|
||||
this.entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.CREATE.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(), account.productId());
|
||||
this.entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.CREATE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
|
||||
account.productId());
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new SavingsCreateBusinessEvent(account));
|
||||
|
||||
@@ -475,8 +476,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId, false);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.APPROVE.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(),
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.APPROVE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
|
||||
savingsAccount.productId());
|
||||
|
||||
final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command, DateUtils.getBusinessLocalDate());
|
||||
@@ -598,8 +599,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId, false);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.REJECTED.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(),
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.REJECTED.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
|
||||
savingsAccount.productId());
|
||||
|
||||
final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command, DateUtils.getBusinessLocalDate());
|
||||
@@ -635,8 +636,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId, false);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVING.getName(),
|
||||
StatusEnum.WITHDRAWN.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(),
|
||||
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
|
||||
StatusEnum.WITHDRAWN.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
|
||||
savingsAccount.productId());
|
||||
|
||||
final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command,
|
||||
|
||||
+5
-1
@@ -21,7 +21,11 @@ package org.apache.fineract.portfolio.search;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SearchConstants {
|
||||
public final class SearchConstants {
|
||||
|
||||
public static final String API_PARAM_COLUMN = "column";
|
||||
|
||||
private SearchConstants() {}
|
||||
|
||||
public enum SearchResponseParameters {
|
||||
|
||||
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* 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.search.service;
|
||||
|
||||
import static java.util.Locale.ENGLISH;
|
||||
import static org.apache.fineract.infrastructure.core.data.ApiParameterError.parameterErrorWithValue;
|
||||
import static org.apache.fineract.infrastructure.core.service.DateUtils.DEFAULT_DATETIME_FORMAT;
|
||||
import static org.apache.fineract.infrastructure.core.service.DateUtils.DEFAULT_DATE_FORMAT;
|
||||
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_MANDATORY;
|
||||
import static org.apache.fineract.portfolio.search.SearchConstants.API_PARAM_COLUMN;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.gson.JsonObject;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
|
||||
public final class SearchUtil {
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 50;
|
||||
|
||||
private static final JsonParserHelper helper = new JsonParserHelper();
|
||||
|
||||
private SearchUtil() {}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, ResultsetColumnHeaderData> mapHeadersToName(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders) {
|
||||
return columnHeaders.stream().collect(Collectors.toMap(ResultsetColumnHeaderData::getColumnName, e -> e));
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData findFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
@NotNull Predicate<ResultsetColumnHeaderData> filter) {
|
||||
return columnHeaders.stream().filter(filter).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData getFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
@NotNull Predicate<ResultsetColumnHeaderData> filter) {
|
||||
ResultsetColumnHeaderData filtered = findFiltered(columnHeaders, filter);
|
||||
if (filtered == null) {
|
||||
throw new PlatformDataIntegrityException("error.msg.column.not.exists", "Column filtered does not exist");
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public static void extractJsonResult(@NotNull SqlRowSet rowSet, @NotNull List<String> selectColumns,
|
||||
@NotNull List<String> resultColumns, @NotNull List<JsonObject> results) {
|
||||
JsonObject json = new JsonObject();
|
||||
for (int i = 0; i < selectColumns.size(); i++) {
|
||||
String sCol = selectColumns.get(i).replaceAll("\"", "");
|
||||
Object rowValue = rowSet.getObject(sCol);
|
||||
if (rowValue != null) {
|
||||
String rCol = resultColumns.get(i);
|
||||
if (rowValue instanceof Character) {
|
||||
json.addProperty(rCol, (Character) rowValue);
|
||||
} else if (rowValue instanceof Number) {
|
||||
json.addProperty(rCol, new BigDecimal(rowValue.toString()));
|
||||
} else if (rowValue instanceof Boolean) {
|
||||
json.addProperty(rCol, (Boolean) rowValue);
|
||||
} else if (rowValue instanceof LocalDateTime) {
|
||||
json.addProperty(rCol, DateUtils.format((LocalDateTime) rowValue));
|
||||
} else if (rowValue instanceof Timestamp) {
|
||||
json.addProperty(rCol, DateUtils.format(((Timestamp) rowValue).toLocalDateTime()));
|
||||
} else if (rowValue instanceof LocalDate) {
|
||||
json.addProperty(rCol, DateUtils.format((LocalDate) rowValue));
|
||||
} else if (rowValue instanceof Date) {
|
||||
json.addProperty(rCol, DateUtils.format(((Date) rowValue).toLocalDate()));
|
||||
} else {
|
||||
json.addProperty(rCol, rowValue.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json.size() > 0) {
|
||||
results.add(json);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> validateToJdbcColumns(List<String> columns, Map<String, ResultsetColumnHeaderData> columnHeaders,
|
||||
List<ApiParameterError> errors, boolean allowEmpty) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (columns == null || columns.isEmpty()) {
|
||||
if (!allowEmpty) {
|
||||
errors.add(parameterErrorWithValue("error.msg.columns.empty", "Columns list is empty", API_PARAM_COLUMN, null));
|
||||
}
|
||||
} else {
|
||||
columns.forEach(rcn -> result.add(validateToJdbcColumn(rcn, columnHeaders, errors, allowEmpty)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String validateToJdbcColumn(String column, Map<String, ResultsetColumnHeaderData> columnHeaders,
|
||||
List<ApiParameterError> errors, boolean allowEmpty) {
|
||||
if (!allowEmpty && column == null) {
|
||||
errors.add(parameterErrorWithValue("error.msg.column.empty", "Column filter is empty", API_PARAM_COLUMN, null));
|
||||
}
|
||||
if (column != null) {
|
||||
SQLInjectionValidator.validateDynamicQuery(column);
|
||||
if (!columnHeaders.containsKey(column)) {
|
||||
column = camelToSnake(column);
|
||||
if (!columnHeaders.containsKey(column)) {
|
||||
errors.add(
|
||||
parameterErrorWithValue("error.msg.invalid.column", "Column not exist in database", API_PARAM_COLUMN, column));
|
||||
}
|
||||
}
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
public static Object parseAndValidateJdbcColumnValue(String column, String columnValue,
|
||||
Map<String, ResultsetColumnHeaderData> columnHeaders, DatabaseType dialect) {
|
||||
List<ApiParameterError> errors = new ArrayList<>();
|
||||
column = validateToJdbcColumn(column, columnHeaders, errors, false);
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(errors);
|
||||
}
|
||||
ResultsetColumnHeaderData columnHeader = columnHeaders.get(column);
|
||||
String dateFormat = columnHeader.getColumnType().isDateTimeType() ? DEFAULT_DATETIME_FORMAT : DEFAULT_DATE_FORMAT;
|
||||
return columnHeader.getColumnType().toJdbcValue(dialect,
|
||||
parseAndValidateColumnValue(columnHeader, columnValue, dateFormat, ENGLISH, dialect), false);
|
||||
}
|
||||
|
||||
public static Object parseAndValidateColumnValue(final ResultsetColumnHeaderData columnHeader, final String pValue,
|
||||
final String dateFormat, final Locale locale, DatabaseType dialect) {
|
||||
String columnValue = pValue;
|
||||
JdbcJavaType colType = columnHeader.getColumnType();
|
||||
if (!colType.isStringType() || !columnHeader.isMandatory()) {
|
||||
columnValue = StringUtils.trimToNull(columnValue);
|
||||
}
|
||||
String errorCode = "validation.msg.validation.errors.exist";
|
||||
String errorMsg = "Validation errors exist.";
|
||||
if (columnValue == null && columnHeader.isMandatory()) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("error.msg.column.mandatory", API_FIELD_MANDATORY,
|
||||
columnHeader.getColumnName());
|
||||
throw new PlatformApiDataValidationException(errorCode, errorMsg, List.of(error));
|
||||
}
|
||||
if (StringUtils.isEmpty(columnValue)) {
|
||||
return columnValue;
|
||||
}
|
||||
SQLInjectionValidator.validateDynamicQuery(columnValue);
|
||||
|
||||
if (columnHeader.hasColumnValues()) {
|
||||
if (columnHeader.isCodeValueDisplayType()) {
|
||||
if (!columnHeader.isColumnValueAllowed(columnValue)) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
|
||||
"Value not found in Allowed Value list", columnHeader.getColumnName(), columnValue);
|
||||
throw new PlatformApiDataValidationException(errorCode, errorMsg, List.of(error));
|
||||
}
|
||||
return columnValue;
|
||||
} else if (columnHeader.isCodeLookupDisplayType()) {
|
||||
final Integer codeLookup = Integer.valueOf(columnValue);
|
||||
if (!columnHeader.isColumnCodeAllowed(codeLookup)) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
|
||||
"Value not found in Allowed Value list", columnHeader.getColumnName(), columnValue);
|
||||
throw new PlatformApiDataValidationException(errorCode, errorMsg, List.of(error));
|
||||
}
|
||||
return codeLookup;
|
||||
} else {
|
||||
throw new PlatformDataIntegrityException("error.msg.invalid.columnType.", "Code: " + columnHeader.getColumnName()
|
||||
+ " - Invalid Type " + colType.getJdbcName(dialect) + " (neither varchar nor int)");
|
||||
}
|
||||
}
|
||||
|
||||
if (colType.isDateType()) {
|
||||
return JsonParserHelper.convertFrom(columnValue, columnHeader.getColumnName(), dateFormat, locale);
|
||||
}
|
||||
if (colType.isDateTimeType()) {
|
||||
return JsonParserHelper.convertDateTimeFrom(columnValue, columnHeader.getColumnName(), dateFormat, locale);
|
||||
}
|
||||
if (colType.isAnyIntegerType()) {
|
||||
return helper.convertToInteger(columnValue, columnHeader.getColumnName(), locale);
|
||||
}
|
||||
if (colType.isDecimalType()) {
|
||||
return helper.convertFrom(columnValue, columnHeader.getColumnName(), locale);
|
||||
}
|
||||
if (colType.isBooleanType()) {
|
||||
final Boolean boolValue = BooleanUtils.toBooleanObject(columnValue);
|
||||
if (boolValue == null) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.boolean.format",
|
||||
"The parameter " + columnHeader.getColumnName() + " has value: " + columnValue + " which is invalid boolean value.",
|
||||
columnHeader.getColumnName(), columnValue);
|
||||
throw new PlatformApiDataValidationException(errorCode, errorMsg, List.of(error));
|
||||
}
|
||||
return boolValue;
|
||||
}
|
||||
if (colType.isStringType()) {
|
||||
if (columnHeader.getColumnLength() > 0 && columnValue.length() > columnHeader.getColumnLength()) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.datatable.entry.column.exceeds.maxlength",
|
||||
"The column `" + columnHeader.getColumnName() + "` exceeds its defined max-length ", columnHeader.getColumnName(),
|
||||
columnValue);
|
||||
throw new PlatformApiDataValidationException(errorCode, errorMsg, List.of(error));
|
||||
}
|
||||
}
|
||||
return columnValue;
|
||||
}
|
||||
|
||||
public static String camelToSnake(final String camelStr) {
|
||||
return camelStr == null ? null
|
||||
: camelStr.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2").replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
}
|
||||
+21
-8
@@ -22,8 +22,10 @@ import static java.util.Collections.emptyList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -31,6 +33,7 @@ import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException;
|
||||
@@ -71,6 +74,7 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testSqlInjectionCaughtQueryDataTable() {
|
||||
mockDatatableValidation();
|
||||
assertThrows(SQLInjectionException.class, () -> {
|
||||
underTest.queryDataTable("table", "cf1", "vf1", "' or 1=1");
|
||||
});
|
||||
@@ -78,6 +82,7 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testSqlInjectionCaughtQueryDataTable2() {
|
||||
mockDatatableValidation();
|
||||
assertThrows(SQLInjectionException.class, () -> {
|
||||
underTest.queryDataTable("table", "cf1", "vf1", "1; DROP TABLE m_loan; SELECT");
|
||||
});
|
||||
@@ -87,18 +92,24 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
public void testQueryDataTableSuccess() {
|
||||
mockDatatableValidation();
|
||||
SqlRowSet sqlRS = Mockito.mock(SqlRowSet.class);
|
||||
when(jdbcTemplate.queryForRowSet(eq("select rc1,rc2 from table where cf1 = ?"), any(Object.class))).thenReturn(sqlRS);
|
||||
when(jdbcTemplate.queryForRowSet(eq("SELECT \"rc1\", \"rc2\" FROM \"table\" WHERE \"cf1\" = ?"), any(Object.class)))
|
||||
.thenReturn(sqlRS);
|
||||
when(sqlRS.next()).thenReturn(true).thenReturn(false);
|
||||
when(sqlRS.getObject(anyString())).thenReturn("value1").thenReturn("value2");
|
||||
when(sqlGenerator.escape(anyString())).thenReturn("rc1").thenReturn("rc2").thenReturn("table").thenReturn("cf1");
|
||||
when(sqlGenerator.buildSelect(anyCollection(), nullable(String.class), eq(false))).thenReturn("SELECT \"rc1\", \"rc2\"");
|
||||
when(sqlGenerator.buildFrom(anyString(), nullable(String.class), eq(false))).thenReturn("FROM \"table\"");
|
||||
when(sqlGenerator.escape(anyString())).thenReturn("\"cf1\"");
|
||||
when(sqlGenerator.alias(anyString(), nullable(String.class))).thenReturn("\"cf1\"");
|
||||
when(databaseTypeResolver.isPostgreSQL()).thenReturn(true);
|
||||
when(databaseTypeResolver.databaseType()).thenReturn(DatabaseType.POSTGRESQL);
|
||||
DatabaseType dialect = databaseTypeResolver.databaseType();
|
||||
|
||||
ResultsetColumnHeaderData cf1 = ResultsetColumnHeaderData.detailed("cf1", "text", 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
ResultsetColumnHeaderData rc1 = ResultsetColumnHeaderData.detailed("rc1", "text", 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
ResultsetColumnHeaderData rc2 = ResultsetColumnHeaderData.detailed("rc2", "text", 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
when(genericDataService.fillResultsetColumnHeaders("table")).thenReturn(List.of(cf1, rc1, rc2));
|
||||
|
||||
List<JsonObject> results = underTest.queryDataTable("table", "cf1", "vf1", "rc1,rc2");
|
||||
@@ -125,12 +136,14 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
@MethodSource("provideParameters")
|
||||
public void testQueryDataTableInvalidParameterError(String columnType, String errorMessage) {
|
||||
mockDatatableValidation();
|
||||
when(databaseTypeResolver.databaseType()).thenReturn(DatabaseType.POSTGRESQL);
|
||||
DatabaseType dialect = databaseTypeResolver.databaseType();
|
||||
ResultsetColumnHeaderData cf1 = ResultsetColumnHeaderData.detailed("cf1", columnType, 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
ResultsetColumnHeaderData rc1 = ResultsetColumnHeaderData.detailed("rc1", "text", 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
ResultsetColumnHeaderData rc2 = ResultsetColumnHeaderData.detailed("rc2", "text", 10L, false, false, emptyList(), null, false,
|
||||
false);
|
||||
false, dialect);
|
||||
when(genericDataService.fillResultsetColumnHeaders("table")).thenReturn(List.of(cf1, rc1, rc2));
|
||||
|
||||
PlatformApiDataValidationException thrown = assertThrows(PlatformApiDataValidationException.class,
|
||||
|
||||
Reference in New Issue
Block a user