FINERACT-1911: Assign Data Table to Transaction - general
This commit is contained in:
+2
-6
@@ -69,16 +69,12 @@ public final class ApiParameterError {
|
||||
|
||||
public static ApiParameterError parameterError(final String globalisationMessageCode, final String defaultUserMessage,
|
||||
final String parameterName, final Object... defaultUserMessageArgs) {
|
||||
final ApiParameterError error = new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs,
|
||||
parameterName, null);
|
||||
return error;
|
||||
return new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs, parameterName, null);
|
||||
}
|
||||
|
||||
public static ApiParameterError parameterErrorWithValue(final String globalisationMessageCode, final String defaultUserMessage,
|
||||
final String parameterName, final String value, final Object... defaultUserMessageArgs) {
|
||||
final ApiParameterError error = new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs,
|
||||
parameterName, value);
|
||||
return error;
|
||||
return new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs, parameterName, value);
|
||||
}
|
||||
|
||||
private ApiParameterError(final String globalisationMessageCode, final String defaultUserMessage, final Object[] defaultUserMessageArgs,
|
||||
|
||||
+110
-5
@@ -25,18 +25,21 @@ import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class DateUtils {
|
||||
|
||||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
||||
public static final String DEFAULT_DATETIME_FORMAT = DEFAULT_DATE_FORMAT + " HH:mm:ss";
|
||||
public static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = new DateTimeFormatterBuilder().appendPattern(DEFAULT_DATETIME_FORMAT)
|
||||
.toFormatter();
|
||||
public static final DateTimeFormatter DEFAULT_DATE_FORMATTER = new DateTimeFormatterBuilder().appendPattern(DEFAULT_DATE_FORMAT)
|
||||
.toFormatter();
|
||||
public static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
|
||||
public static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT);
|
||||
|
||||
private DateUtils() {
|
||||
|
||||
@@ -86,4 +89,106 @@ public final class DateUtils {
|
||||
public static long getDifferenceInDays(final LocalDate localDateBefore, final LocalDate localDateAfter) {
|
||||
return DAYS.between(localDateBefore, localDateAfter);
|
||||
}
|
||||
|
||||
public static int compareToBusinessDate(LocalDate date) {
|
||||
return compare(date, getBusinessLocalDate());
|
||||
}
|
||||
|
||||
public static boolean isEqualBusinessDate(LocalDate date) {
|
||||
return isEqual(date, getBusinessLocalDate());
|
||||
}
|
||||
|
||||
public static boolean isBeforeBusinessDate(LocalDate date) {
|
||||
return isBefore(date, getBusinessLocalDate());
|
||||
}
|
||||
|
||||
public static boolean isAfterBusinessDate(LocalDate date) {
|
||||
return isAfter(date, getBusinessLocalDate());
|
||||
}
|
||||
|
||||
public static int compare(LocalDate first, LocalDate second) {
|
||||
return first == null ? (second == null ? 0 : -1) : first.compareTo(second);
|
||||
}
|
||||
|
||||
public static boolean isEqual(LocalDate first, LocalDate second) {
|
||||
return first == null ? second == null : (second != null && first.isEqual(second));
|
||||
}
|
||||
|
||||
public static boolean isBefore(LocalDate first, LocalDate second) {
|
||||
return second != null && (first == null || first.isBefore(second));
|
||||
}
|
||||
|
||||
public static boolean isAfter(LocalDate first, LocalDate second) {
|
||||
return first != null && (second == null || first.isAfter(second));
|
||||
}
|
||||
|
||||
public static LocalDate parseLocalDate(String stringDate) {
|
||||
return parseLocalDate(stringDate, null);
|
||||
}
|
||||
|
||||
public static LocalDate parseLocalDate(String stringDate, String format) {
|
||||
return parseLocalDate(stringDate, format, null);
|
||||
}
|
||||
|
||||
public static LocalDate parseLocalDate(String stringDate, String format, Locale locale) {
|
||||
if (stringDate == null) {
|
||||
return null;
|
||||
}
|
||||
DateTimeFormatter formatter = getDateFormatter(format, locale);
|
||||
try {
|
||||
return LocalDate.parse(stringDate, formatter);
|
||||
} catch (final DateTimeParseException e) {
|
||||
final List<ApiParameterError> errors = List.of(ApiParameterError.parameterError("validation.msg.invalid.date.pattern",
|
||||
"The parameter date (" + stringDate + ") format is invalid", "date", stringDate));
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String format(LocalDate date) {
|
||||
return format(date, null);
|
||||
}
|
||||
|
||||
public static String format(LocalDate date, String format) {
|
||||
return format(date, format, null);
|
||||
}
|
||||
|
||||
public static String format(LocalDate date, String format, Locale locale) {
|
||||
return date == null ? null : date.format(getDateFormatter(format, locale));
|
||||
}
|
||||
|
||||
public static String format(LocalDateTime dateTime) {
|
||||
return format(dateTime, null);
|
||||
}
|
||||
|
||||
public static String format(LocalDateTime dateTime, String format) {
|
||||
return format(dateTime, format, null);
|
||||
}
|
||||
|
||||
public static String format(LocalDateTime dateTime, String format, Locale locale) {
|
||||
return dateTime == null ? null : dateTime.format(getDateTimeFormatter(format, locale));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DateTimeFormatter getDateFormatter(String format, Locale locale) {
|
||||
DateTimeFormatter formatter = DEFAULT_DATE_FORMATTER;
|
||||
if (format != null || locale != null) {
|
||||
if (format == null) {
|
||||
format = DEFAULT_DATE_FORMAT;
|
||||
}
|
||||
formatter = locale == null ? DateTimeFormatter.ofPattern(format) : DateTimeFormatter.ofPattern(format, locale);
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DateTimeFormatter getDateTimeFormatter(String format, Locale locale) {
|
||||
DateTimeFormatter formatter = DEFAULT_DATETIME_FORMATTER;
|
||||
if (format != null || locale != null) {
|
||||
if (format == null) {
|
||||
format = DEFAULT_DATETIME_FORMAT;
|
||||
}
|
||||
formatter = locale == null ? DateTimeFormatter.ofPattern(format) : DateTimeFormatter.ofPattern(format, locale);
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.interoperation.util;
|
||||
package org.apache.fineract.infrastructure.core.service;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
@@ -31,6 +31,10 @@ public final class MathUtil {
|
||||
|
||||
}
|
||||
|
||||
public static <E extends Number> E nullToDefault(E value, E def) {
|
||||
return value == null ? def : value;
|
||||
}
|
||||
|
||||
public static Long nullToZero(Long value) {
|
||||
return nullToDefault(value, 0L);
|
||||
}
|
||||
@@ -328,6 +332,10 @@ public final class MathUtil {
|
||||
return isEmpty(amount) ? amount : amount.negate(mc);
|
||||
}
|
||||
|
||||
public static String formatToSql(BigDecimal amount) {
|
||||
return amount == null ? null : amount.toPlainString();
|
||||
}
|
||||
|
||||
// ----------------- Money -----------------
|
||||
|
||||
public static Money nullToZero(Money value, @NotNull MonetaryCurrency currency) {
|
||||
+58
-2
@@ -20,8 +20,14 @@ package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@@ -36,14 +42,19 @@ public class DatabaseSpecificSQLGenerator {
|
||||
}
|
||||
|
||||
public String escape(String arg) {
|
||||
if (databaseTypeResolver.isMySQL()) {
|
||||
DatabaseType dialect = databaseTypeResolver.databaseType();
|
||||
if (dialect.isMySql()) {
|
||||
return format("`%s`", arg);
|
||||
} else if (databaseTypeResolver.isPostgreSQL()) {
|
||||
} else if (dialect.isPostgres()) {
|
||||
return format("\"%s\"", arg);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
public String formatValue(JdbcJavaType columnType, String value) {
|
||||
return (columnType.isStringType() || columnType.isAnyDateType()) ? format("'%s'", value) : value;
|
||||
}
|
||||
|
||||
public String groupConcat(String arg) {
|
||||
if (databaseTypeResolver.isMySQL()) {
|
||||
return format("GROUP_CONCAT(%s)", arg);
|
||||
@@ -189,4 +200,49 @@ public class DatabaseSpecificSQLGenerator {
|
||||
throw new IllegalStateException("Database type is not supported for casting to json " + databaseTypeResolver.databaseType());
|
||||
}
|
||||
}
|
||||
|
||||
public String alias(@NotNull String field, String alias) {
|
||||
return Strings.isEmpty(alias) ? field : (alias + '.') + field;
|
||||
}
|
||||
|
||||
public String buildSelect(Collection<String> fields, String alias, boolean embedded) {
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String select = "";
|
||||
if (!embedded) {
|
||||
select = "SELECT ";
|
||||
}
|
||||
return select + fields.stream().map(e -> alias(escape(e), alias)).collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
public String buildFrom(String definition, String alias, boolean embedded) {
|
||||
if (definition == null) {
|
||||
return "";
|
||||
}
|
||||
String from = "";
|
||||
if (!embedded) {
|
||||
from = "FROM ";
|
||||
}
|
||||
return from + escape(definition) + (Strings.isEmpty(alias) ? "" : (" " + alias));
|
||||
}
|
||||
|
||||
public String buildJoin(@NotNull String definition, String alias, @NotNull String fkCol, String refAlias, @NotNull String refCol,
|
||||
String joinType) {
|
||||
String join = Strings.isEmpty(joinType) ? "JOIN" : (joinType + " JOIN");
|
||||
alias = Strings.isEmpty(alias) ? "" : (" " + alias);
|
||||
return format("%s %s%s ON %s = %s", join, escape(definition), alias, alias(escape(fkCol), alias), alias(escape(refCol), refAlias));
|
||||
}
|
||||
|
||||
public String buildOrderBy(List<Sort.Order> orders, String alias, boolean embedded) {
|
||||
if (orders == null || orders.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String orderBy = "";
|
||||
if (!embedded) {
|
||||
orderBy = "ORDER BY ";
|
||||
}
|
||||
return orderBy + orders.stream().map(e -> String.join(" ", alias(escape(e.getProperty()), alias), e.getDirection().name()))
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -19,5 +19,16 @@
|
||||
package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
public enum DatabaseType {
|
||||
MYSQL, POSTGRESQL
|
||||
|
||||
MYSQL, //
|
||||
POSTGRESQL, //
|
||||
;
|
||||
|
||||
public boolean isMySql() {
|
||||
return this == MYSQL;
|
||||
}
|
||||
|
||||
public boolean isPostgres() {
|
||||
return this == POSTGRESQL;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -58,10 +58,10 @@ public class DatabaseTypeResolver implements InitializingBean {
|
||||
}
|
||||
|
||||
public boolean isPostgreSQL() {
|
||||
return DatabaseType.POSTGRESQL.equals(currentDatabaseType.get());
|
||||
return DatabaseType.POSTGRESQL == currentDatabaseType.get();
|
||||
}
|
||||
|
||||
public boolean isMySQL() {
|
||||
return DatabaseType.MYSQL.equals(currentDatabaseType.get());
|
||||
return DatabaseType.MYSQL == currentDatabaseType.get();
|
||||
}
|
||||
}
|
||||
|
||||
+371
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
public enum JavaType {
|
||||
|
||||
BOOLEAN(boolean.class), //
|
||||
BYTE(byte.class), //
|
||||
SHORT(short.class), //
|
||||
INT(int.class), //
|
||||
LONG(long.class), //
|
||||
CHAR(char.class), //
|
||||
FLOAT(float.class), //
|
||||
DOUBLE(double.class), //
|
||||
// non-primitive types
|
||||
BOOLEAN_OBJ(Boolean.class), //
|
||||
BYTE_OBJ(Byte.class), //
|
||||
SHORT_OBJ(Short.class), //
|
||||
INT_OBJ(Integer.class), //
|
||||
LONG_OBJ(Long.class), //
|
||||
CHAR_OBJ(Character.class), //
|
||||
FLOAT_OBJ(Float.class), //
|
||||
DOUBLE_OBJ(Double.class), //
|
||||
BIGDECIMAL(BigDecimal.class), //
|
||||
BIGINTEGER(BigInteger.class), //
|
||||
NUMBER(Number.class), //
|
||||
STRING(String.class), //
|
||||
DATETIME(Timestamp.class), //
|
||||
TIME(Time.class), //
|
||||
DATE(Date.class), //
|
||||
// Java8 time API
|
||||
LOCAL_DATE(LocalDate.class), //
|
||||
LOCAL_TIME(LocalTime.class), //
|
||||
LOCAL_DATETIME(LocalDateTime.class), //
|
||||
OFFSET_TIME(OffsetTime.class), //
|
||||
OFFSET_DATETIME(OffsetDateTime.class), //
|
||||
|
||||
BINARY(byte[].class), //
|
||||
ARRAY(null), //
|
||||
COLLECTION(Collection.class), //
|
||||
MAP(Map.class), //
|
||||
PC(null), //
|
||||
LOCALE(Locale.class), //
|
||||
PC_UNTYPED(Persistable.class), //
|
||||
CALENDAR(Calendar.class), //
|
||||
OID(null), //
|
||||
INPUT_STREAM(InputStream.class), //
|
||||
INPUT_READER(Reader.class), //
|
||||
ENUM(Enum.class), //
|
||||
OBJECT(Object.class), //
|
||||
;
|
||||
|
||||
public static final JavaType[] VALUES = values();
|
||||
|
||||
private static final Map<Class<?>, JavaType> BY_TYPE = Arrays.stream(VALUES).filter(e -> e.type != null)
|
||||
.collect(Collectors.toMap(JavaType::getTypeClass, v -> v));
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
JavaType(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<?> getTypeClass() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return ordinal() <= DOUBLE.ordinal();
|
||||
}
|
||||
|
||||
public boolean isBooleanType() {
|
||||
return this == BOOLEAN || this == BOOLEAN_OBJ;
|
||||
}
|
||||
|
||||
public boolean isByteType() {
|
||||
return this == BYTE || this == BYTE_OBJ;
|
||||
}
|
||||
|
||||
public boolean isShortType() {
|
||||
return this == SHORT || this == SHORT_OBJ;
|
||||
}
|
||||
|
||||
public boolean isIntegerType() {
|
||||
return this == INT || this == INT_OBJ;
|
||||
}
|
||||
|
||||
public boolean isLongType() {
|
||||
return this == LONG || this == LONG_OBJ;
|
||||
}
|
||||
|
||||
public boolean isAnyIntegerType() {
|
||||
return isByteType() || isShortType() || isIntegerType() || isLongType() || this == BIGINTEGER;
|
||||
}
|
||||
|
||||
public boolean isFloatType() {
|
||||
return this == FLOAT || this == FLOAT_OBJ;
|
||||
}
|
||||
|
||||
public boolean isDoubleType() {
|
||||
return this == DOUBLE || this == DOUBLE_OBJ;
|
||||
}
|
||||
|
||||
public boolean isAnyFloatType() {
|
||||
return isFloatType() || isDoubleType();
|
||||
}
|
||||
|
||||
public boolean isDecimalType() {
|
||||
return this == BIGDECIMAL;
|
||||
}
|
||||
|
||||
public boolean isObjectNumericType() {
|
||||
return Number.class.isAssignableFrom(getTypeClass());
|
||||
}
|
||||
|
||||
public boolean isPrimitiveNumericType() {
|
||||
return isPrimitive() && this != BOOLEAN && this != CHAR;
|
||||
}
|
||||
|
||||
public boolean isNumericType() {
|
||||
return isPrimitiveNumericType() || isObjectNumericType();
|
||||
}
|
||||
|
||||
public boolean isStringType() {
|
||||
return this == STRING;
|
||||
}
|
||||
|
||||
public boolean isCharacterType() {
|
||||
return this == CHAR || this == CHAR_OBJ;
|
||||
}
|
||||
|
||||
public boolean isDateType() {
|
||||
return this == DATE || this == LOCAL_DATE;
|
||||
}
|
||||
|
||||
public boolean isTimeType() {
|
||||
return this == TIME || this == LOCAL_TIME || this == OFFSET_TIME;
|
||||
}
|
||||
|
||||
public boolean isDateTimeType() {
|
||||
return this == DATETIME || this == LOCAL_DATETIME || this == OFFSET_DATETIME;
|
||||
}
|
||||
|
||||
public boolean isAnyDateType() {
|
||||
return isDateType() || isTimeType() || isDateTimeType();
|
||||
}
|
||||
|
||||
public boolean isBinaryType() {
|
||||
return this == BINARY;
|
||||
}
|
||||
|
||||
public JavaType getPrimitiveType() {
|
||||
if (isPrimitive()) {
|
||||
return this;
|
||||
}
|
||||
return switch (this) {
|
||||
case BOOLEAN_OBJ -> BOOLEAN;
|
||||
case BYTE_OBJ -> BYTE;
|
||||
case SHORT_OBJ -> SHORT;
|
||||
case INT_OBJ -> INT;
|
||||
case LONG_OBJ -> LONG;
|
||||
case CHAR_OBJ -> CHAR;
|
||||
case DOUBLE_OBJ -> DOUBLE;
|
||||
case FLOAT_OBJ -> FLOAT;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JavaType getObjectType() {
|
||||
if (!isPrimitive()) {
|
||||
return this;
|
||||
}
|
||||
return switch (this) {
|
||||
case BOOLEAN -> BOOLEAN_OBJ;
|
||||
case BYTE -> BYTE_OBJ;
|
||||
case SHORT -> SHORT_OBJ;
|
||||
case INT -> INT_OBJ;
|
||||
case LONG -> LONG_OBJ;
|
||||
case CHAR -> CHAR_OBJ;
|
||||
case DOUBLE -> DOUBLE_OBJ;
|
||||
case FLOAT -> FLOAT_OBJ;
|
||||
default ->
|
||||
throw new PlatformServiceUnavailableException("error.msg.database.type.not.allowed", "Unknown primitive type " + this);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the field metadata type for the given class. First class objects are not recognized in this method.
|
||||
*/
|
||||
@NotNull
|
||||
public static JavaType forType(Class<?> type) {
|
||||
if (type == null) {
|
||||
return OBJECT;
|
||||
}
|
||||
JavaType javaType = BY_TYPE.get(type);
|
||||
if (javaType != null) {
|
||||
return javaType;
|
||||
}
|
||||
if (type.isArray()) {
|
||||
return ARRAY;
|
||||
}
|
||||
// have to do this first to catch custom collection and map types;
|
||||
// on resolve we figure out if these custom types are
|
||||
// persistence-capable
|
||||
for (Class<?> aType : BY_TYPE.keySet()) {
|
||||
if (aType.isAssignableFrom(type)) {
|
||||
return BY_TYPE.get(aType);
|
||||
}
|
||||
}
|
||||
|
||||
if (type.isInterface()) {
|
||||
return Serializable.class.isAssignableFrom(type) ? OBJECT : PC_UNTYPED;
|
||||
}
|
||||
if (type.isAssignableFrom(Reader.class)) {
|
||||
return INPUT_READER;
|
||||
}
|
||||
if (type.isAssignableFrom(InputStream.class)) {
|
||||
return INPUT_STREAM;
|
||||
}
|
||||
return OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the field metadata type for the given class. First class objects are not recognized in this method.
|
||||
*/
|
||||
public boolean matchType(Class<?> type) {
|
||||
return matchType(type, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the field metadata type for the given class. First class objects are not recognized in this method.
|
||||
*/
|
||||
public boolean matchType(Class<?> type, boolean strict) {
|
||||
return matchType(type, strict, false);
|
||||
}
|
||||
|
||||
private boolean matchType(Class<?> type, boolean strict, boolean embedded) {
|
||||
if (type == null) {
|
||||
return this == OBJECT;
|
||||
}
|
||||
if (this.type == null) {
|
||||
return false;
|
||||
}
|
||||
if (type.equals(this.type)) {
|
||||
return true;
|
||||
}
|
||||
if (type.isArray()) {
|
||||
return this.type.isArray();
|
||||
}
|
||||
if (this.type.isAssignableFrom(type)) {
|
||||
return true;
|
||||
}
|
||||
if (type.isInterface()) {
|
||||
return Serializable.class.isAssignableFrom(type) ? this == OBJECT : this == PC_UNTYPED;
|
||||
}
|
||||
if (type.isAssignableFrom(Reader.class)) {
|
||||
return this == INPUT_READER;
|
||||
}
|
||||
if (type.isAssignableFrom(InputStream.class)) {
|
||||
return this == INPUT_STREAM;
|
||||
}
|
||||
if (!strict && !embedded) {
|
||||
JavaType thisType = this;
|
||||
if (isPrimitive() && !type.isPrimitive()) {
|
||||
thisType = getObjectType();
|
||||
} else if (type.isPrimitive()) {
|
||||
JavaType primitiveType = getPrimitiveType();
|
||||
if (primitiveType != null) {
|
||||
thisType = primitiveType;
|
||||
}
|
||||
}
|
||||
if (this != thisType && thisType.matchType(type, false, true)) {
|
||||
return true;
|
||||
}
|
||||
if (Number.class.isAssignableFrom(type) && isNumericType()) {
|
||||
JavaType thatType = forType(type);
|
||||
if (thisType.isAnyIntegerType() && thatType.isAnyIntegerType()) {
|
||||
return thatType.ordinal() <= thisType.ordinal();
|
||||
}
|
||||
if (thisType.isAnyFloatType() && thatType.isAnyFloatType()) {
|
||||
return thatType.ordinal() <= thisType.ordinal();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this == OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the provided Object value is the default for this java type. For example: If o = Integer(0) and
|
||||
* typeCode = JavaTypes.INT, this method will return true.
|
||||
*/
|
||||
public boolean isDefault(Object o) {
|
||||
return switch (this) {
|
||||
case BOOLEAN, BOOLEAN_OBJ -> Boolean.FALSE.equals(o);
|
||||
case BYTE, BYTE_OBJ -> o != null && ((Byte) o) == 0;
|
||||
case SHORT, SHORT_OBJ -> o != null && ((Short) o) == 0;
|
||||
case INT, INT_OBJ -> o != null && ((Integer) o) == 0;
|
||||
case LONG, LONG_OBJ -> o != null && ((Long) o) == 0L;
|
||||
case CHAR, CHAR_OBJ -> o != null && ((Character) o) == '\u0000';
|
||||
case FLOAT, FLOAT_OBJ -> o != null && ((Float) o) == 0.0F;
|
||||
case DOUBLE, DOUBLE_OBJ -> o != null && ((Double) o) == 0.0d;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public Object parse(String s, String format, Locale locale) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return switch (this) {
|
||||
case BOOLEAN, BOOLEAN_OBJ -> Boolean.valueOf(s);
|
||||
case BYTE, BYTE_OBJ -> Byte.parseByte(s);
|
||||
case SHORT, SHORT_OBJ -> Short.parseShort(s);
|
||||
case INT, INT_OBJ -> Integer.parseInt(s);
|
||||
case LONG, LONG_OBJ -> Long.parseLong(s);
|
||||
case CHAR, CHAR_OBJ -> StringUtils.isEmpty(s) ? null : s.charAt(0);
|
||||
case FLOAT, FLOAT_OBJ -> Float.parseFloat(s);
|
||||
case DOUBLE, DOUBLE_OBJ -> Double.parseDouble(s);
|
||||
case BIGDECIMAL -> new BigDecimal(s, MoneyHelper.getMathContext());
|
||||
case BIGINTEGER -> new BigInteger(s);
|
||||
case STRING -> s;
|
||||
case DATETIME -> Timestamp.valueOf(s);
|
||||
case TIME -> Time.valueOf(s);
|
||||
case DATE -> new Date(s);
|
||||
default -> throw new PlatformServiceUnavailableException("error.msg.database.type.parse",
|
||||
"Parse string representation is not supported " + this);
|
||||
};
|
||||
}
|
||||
}
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.sql.JDBCType;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
|
||||
public enum JdbcJavaType {
|
||||
|
||||
// Mysql, PostgreSql
|
||||
BIT(JavaType.BOOLEAN, new DialectType(JDBCType.BIT), new DialectType(JDBCType.BIT, true)) { //
|
||||
|
||||
@Override
|
||||
public Object toJdbcValueImpl(@NotNull DatabaseType dialect, Object value) {
|
||||
return Boolean.TRUE.equals(value) ? 1 : 0;
|
||||
}
|
||||
},
|
||||
BOOLEAN(JavaType.BOOLEAN, new DialectType(JDBCType.BIT), new DialectType(JDBCType.BOOLEAN, null, "BOOL")) { //
|
||||
|
||||
@Override
|
||||
public Object toJdbcValueImpl(@NotNull DatabaseType dialect, Object value) {
|
||||
return dialect.isMySql() ? (Boolean.TRUE.equals(value) ? 1 : 0) : super.toJdbcValueImpl(dialect, value);
|
||||
}
|
||||
},
|
||||
SMALLINT(JavaType.SHORT, new DialectType(JDBCType.SMALLINT, true), new DialectType(JDBCType.SMALLINT, null, "INT2")), //
|
||||
TINYINT(JavaType.SHORT, new DialectType(JDBCType.TINYINT, true), new DialectType(JDBCType.SMALLINT)) { //
|
||||
|
||||
@Override
|
||||
public Object toJdbcValueImpl(@NotNull DatabaseType dialect, Object value) {
|
||||
return dialect.isMySql() && value instanceof Boolean ? (Boolean.TRUE.equals(value) ? 1 : 0)
|
||||
: super.toJdbcValueImpl(dialect, value);
|
||||
}
|
||||
},
|
||||
INTEGER(JavaType.INT, new DialectType(JDBCType.INTEGER, null, true, "INT"), new DialectType(JDBCType.INTEGER, null, "INT", "INT4")), //
|
||||
MEDIUMINT(JavaType.INT, new DialectType(JDBCType.INTEGER, "MEDIUMINT", true), new DialectType(JDBCType.INTEGER)), //
|
||||
BIGINT(JavaType.LONG, new DialectType(JDBCType.BIGINT, true), new DialectType(JDBCType.BIGINT, null, "INT8")), //
|
||||
REAL(JavaType.FLOAT, new DialectType(JDBCType.FLOAT, true, true), new DialectType(JDBCType.REAL)), //
|
||||
FLOAT(JavaType.FLOAT, new DialectType(JDBCType.FLOAT, true, true), new DialectType(JDBCType.REAL, null, "FLOAT4")), //
|
||||
DOUBLE(JavaType.DOUBLE, new DialectType(JDBCType.DOUBLE, null, true, true, "DOUBLE PRECISION", "REAL"),
|
||||
new DialectType(JDBCType.DOUBLE, "DOUBLE PRECISION", "FLOAT8")), //
|
||||
NUMERIC(JavaType.BIGDECIMAL, new DialectType(JDBCType.NUMERIC, true, true), new DialectType(JDBCType.NUMERIC, true, true)), //
|
||||
DECIMAL(JavaType.BIGDECIMAL, new DialectType(JDBCType.DECIMAL, "DECIMAL", true, true, "DEC", "FIXED"),
|
||||
new DialectType(JDBCType.DECIMAL, true, true)), //
|
||||
SERIAL(JavaType.INT, new DialectType(JDBCType.INTEGER, "{type} AUTO_INCREMENT", true, false),
|
||||
new DialectType(JDBCType.INTEGER, "SERIAL", "SERIAL4")), //
|
||||
SMALLSERIAL(JavaType.SHORT, new DialectType(JDBCType.SMALLINT, "{type} AUTO_INCREMENT", true, false),
|
||||
new DialectType(JDBCType.SMALLINT, "SMALLSERIAL")), //
|
||||
BIGSERIAL(JavaType.LONG, new DialectType(JDBCType.BIGINT, "{type} AUTO_INCREMENT", true),
|
||||
new DialectType(JDBCType.BIGINT, "BIGSERIAL", "SERIAL8")), //
|
||||
MONEY(JavaType.BIGDECIMAL, new DialectType(JDBCType.DECIMAL, "DECIMAL(19,2)"), new DialectType(JDBCType.DECIMAL, "MONEY")), //
|
||||
CHAR(JavaType.STRING, new DialectType(JDBCType.CHAR, true), new DialectType(JDBCType.CHAR, null, true, "CHARACTER", "BPCHAR")), //
|
||||
VARCHAR(JavaType.STRING, new DialectType(JDBCType.VARCHAR, true), new DialectType(JDBCType.VARCHAR, null, true, "CHARACTER VARYING")), //
|
||||
LONGVARCHAR(JavaType.STRING, new DialectType(JDBCType.VARCHAR, true), new DialectType(JDBCType.VARCHAR, true)), //
|
||||
TEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "TEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
|
||||
TINYTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "TINYTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
|
||||
MEDIUMTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "MEDIUMTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
|
||||
LONGTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "LONGTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
|
||||
DATE(JavaType.LOCAL_DATE, new DialectType(JDBCType.DATE), new DialectType(JDBCType.DATE)), //
|
||||
// precision for TIME, TIMESTAMP (postgres) and INTERVAL specifies the number of fractional digits retained in the
|
||||
// seconds field, but by default, there is no explicit bound on precision
|
||||
TIME(JavaType.LOCAL_TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, null, "TIME WITHOUT TIME ZONE")), //
|
||||
TIME_WITH_TIMEZONE(JavaType.OFFSET_TIME, new DialectType(JDBCType.TIME_WITH_TIMEZONE, "TIME"),
|
||||
new DialectType(JDBCType.TIME_WITH_TIMEZONE, null, "TIME WITH TIME ZONE")), //
|
||||
DATETIME(JavaType.LOCAL_DATETIME, new DialectType(JDBCType.TIMESTAMP, "DATETIME", "TIMESTAMP"),
|
||||
new DialectType(JDBCType.TIMESTAMP, null, "TIMESTAMP WITHOUT TIME ZONE")), //
|
||||
TIMESTAMP_WITH_TIMEZONE(JavaType.OFFSET_DATETIME, new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, null, "DATETIME"),
|
||||
new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, null, "TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ")), //
|
||||
INTERVAL(JavaType.TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, "INTERVAL")), //
|
||||
BINARY(JavaType.BINARY, new DialectType(JDBCType.BINARY, true), new DialectType(JDBCType.BINARY, "BYTEA")), //
|
||||
VARBINARY(JavaType.BINARY, new DialectType(JDBCType.VARBINARY, true), new DialectType(JDBCType.VARBINARY, "BYTEA")), //
|
||||
LONGVARBINARY(JavaType.BINARY, new DialectType(JDBCType.VARBINARY, true), new DialectType(JDBCType.VARBINARY, "BYTEA")), //
|
||||
BYTEA(JavaType.BINARY, new DialectType(JDBCType.BLOB, "LONGBLOB"), new DialectType(JDBCType.BLOB, "BYTEA")), //
|
||||
BLOB(JavaType.BINARY, new DialectType(JDBCType.BLOB, "BLOB"), new DialectType(JDBCType.BLOB, "BYTEA")), //
|
||||
TINYBLOB(JavaType.BINARY, new DialectType(JDBCType.BLOB, "TINYBLOB"), new DialectType(JDBCType.BLOB, "BYTEA")), //
|
||||
MEDIUMBLOB(JavaType.BINARY, new DialectType(JDBCType.BLOB, "MEDIUMBLOB"), new DialectType(JDBCType.BLOB, "BYTEA")), //
|
||||
LONGBLOB(JavaType.BINARY, new DialectType(JDBCType.BLOB, "LONGBLOB"), new DialectType(JDBCType.BLOB, "BYTEA")), //
|
||||
;
|
||||
|
||||
// /* Not used types */
|
||||
// /* *** java.sql.JDBCType *** */
|
||||
// NULL, OTHER, JAVA_OBJECT, DISTINCT, STRUCT, ARRAY, CLOB, REF, DATALINK
|
||||
// /* JDBC 4.0 Types */
|
||||
// ROWID, NCHAR(Types.NCHAR), NVARCHAR, NCLOB, SQLXML
|
||||
// /* JDBC 4.2 Types */
|
||||
// REF_CURSOR, TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE
|
||||
// /* *** POSTGRES *** */
|
||||
// bit varying [(n)] / varbit, box, cidr, circle, inet, interval, line, lseg, macaddr, path, point, polygon, time
|
||||
// [(p)] with time zone, timez,
|
||||
// timestamp [(p)] with time zone, timestamptz, bit(n), bit varying(n), array[n]
|
||||
// /* *** MYSQL *** */
|
||||
// year, enum, set, spatial types, JSON
|
||||
|
||||
private static final String PLACEHOLDER_TYPE = "{type}";
|
||||
|
||||
@NotNull
|
||||
private final JavaType javaType;
|
||||
private final ImmutableList<DialectType> dialectTypes;
|
||||
|
||||
JdbcJavaType(@NotNull JavaType javaType, DialectType... dialectTypes) {
|
||||
this.javaType = javaType;
|
||||
this.dialectTypes = ImmutableList.copyOf(dialectTypes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JavaType getJavaType() {
|
||||
return javaType;
|
||||
}
|
||||
|
||||
public static JdbcJavaType getByTypeName(@NotNull DatabaseType dialect, String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
name = name.toUpperCase();
|
||||
|
||||
for (JdbcJavaType type : values()) {
|
||||
DialectType dialectType = type.getDialectType(dialect);
|
||||
if (dialectType.getNameResolved().equals(name)) {
|
||||
return type;
|
||||
}
|
||||
if (dialectType.alterNames != null) {
|
||||
for (String alterName : dialectType.alterNames) {
|
||||
if (alterName.equals(name)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DialectType getDialectType(@NotNull DatabaseType dialect) {
|
||||
DialectType dialectType = dialectTypes.get(dialect.ordinal());
|
||||
if (dialectType == null) {
|
||||
throw new PlatformServiceUnavailableException("error.msg.database.dialect.not.allowed",
|
||||
"Dialect " + dialect + " is not supported for " + this);
|
||||
}
|
||||
return dialectType;
|
||||
}
|
||||
|
||||
public boolean isBooleanType() {
|
||||
return getJavaType().isBooleanType();
|
||||
}
|
||||
|
||||
public boolean canBooleanType(@NotNull DatabaseType dialect) {
|
||||
return isBooleanType() || (dialect.isMySql() && this == TINYINT);
|
||||
}
|
||||
|
||||
public boolean isStringType() {
|
||||
// ("char", "varchar", "blob", "text", "tinyblob", "tinytext", "mediumblob", "mediumtext", "longblob",
|
||||
// "longtext")
|
||||
return getJavaType().isStringType();
|
||||
}
|
||||
|
||||
public boolean isVarcharType() {
|
||||
// ("char", "varchar", "blob", "text", "tinyblob", "tinytext", "mediumblob", "mediumtext", "longblob",
|
||||
// "longtext")
|
||||
return this == VARCHAR || this == LONGVARCHAR;
|
||||
}
|
||||
|
||||
public boolean isTextType() {
|
||||
return this == TEXT || this == TINYTEXT || this == MEDIUMTEXT || this == LONGTEXT;
|
||||
}
|
||||
|
||||
public boolean isSerialType() {
|
||||
return this == SERIAL || this == SMALLSERIAL || this == BIGSERIAL;
|
||||
}
|
||||
|
||||
public boolean isIntegerType() {
|
||||
return getJavaType().isIntegerType();
|
||||
}
|
||||
|
||||
public boolean isAnyIntegerType() {
|
||||
return getJavaType().isAnyIntegerType();
|
||||
}
|
||||
|
||||
public boolean isFloatType() {
|
||||
return getJavaType().isFloatType();
|
||||
}
|
||||
|
||||
public boolean isAnyFloatType() {
|
||||
return getJavaType().isAnyFloatType();
|
||||
}
|
||||
|
||||
public boolean isDecimalType() {
|
||||
return getJavaType().isDecimalType();
|
||||
}
|
||||
|
||||
public boolean isNumericType() {
|
||||
return getJavaType().isNumericType();
|
||||
}
|
||||
|
||||
public boolean isDateType() {
|
||||
return getJavaType().isDateType();
|
||||
}
|
||||
|
||||
public boolean isTimeType() {
|
||||
return getJavaType().isTimeType();
|
||||
}
|
||||
|
||||
public boolean isDateTimeType() {
|
||||
return getJavaType().isDateTimeType();
|
||||
}
|
||||
|
||||
public boolean isAnyDateType() {
|
||||
return getJavaType().isAnyDateType();
|
||||
}
|
||||
|
||||
public boolean isBlobType() {
|
||||
return this == TINYBLOB || this == MEDIUMBLOB || this == BLOB || this == LONGBLOB;
|
||||
}
|
||||
|
||||
public boolean isBinaryType() {
|
||||
return getJavaType().isBinaryType();
|
||||
}
|
||||
|
||||
public boolean hasPrecision(@NotNull DatabaseType dialect) {
|
||||
return getDialectType(dialect).precision;
|
||||
}
|
||||
|
||||
public boolean hasScale(@NotNull DatabaseType dialect) {
|
||||
return getDialectType(dialect).scale;
|
||||
}
|
||||
|
||||
public String getJdbcName(@NotNull DatabaseType dialect) {
|
||||
DialectType dialectType = getDialectType(dialect);
|
||||
return dialectType.getNameResolved();
|
||||
}
|
||||
|
||||
public String formatSql(@NotNull DatabaseType dialect, Integer precision) {
|
||||
return formatSql(dialect, precision, null);
|
||||
}
|
||||
|
||||
public String formatSql(@NotNull DatabaseType dialect) {
|
||||
return formatSql(dialect, null, null);
|
||||
}
|
||||
|
||||
public String formatSql(@NotNull DatabaseType dialect, Integer precision, Integer scale) {
|
||||
DialectType dialectType = getDialectType(dialect);
|
||||
return dialectType.formatSql(precision, scale);
|
||||
}
|
||||
|
||||
public Object toJdbcValue(@NotNull DatabaseType dialect, Object value, boolean check) {
|
||||
if (value != null && check && !javaType.getObjectType().matchType(value.getClass(), false)) {
|
||||
throw new PlatformServiceUnavailableException("error.msg.database.type.not.allowed",
|
||||
"Data type of parameter " + value + " does not match " + this);
|
||||
}
|
||||
return toJdbcValueImpl(dialect, value);
|
||||
}
|
||||
|
||||
public Object toJdbcValueImpl(@NotNull DatabaseType dialect, Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@com.google.errorprone.annotations.Immutable
|
||||
private static final class DialectType {
|
||||
|
||||
@NotNull
|
||||
private final JDBCType jdbcType;
|
||||
@NotNull
|
||||
private final String name;
|
||||
private final boolean precision;
|
||||
private final boolean scale;
|
||||
private final ImmutableList<String> alterNames;
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, String name, boolean precision, boolean scale, String... alterNames) {
|
||||
this.jdbcType = jdbcType;
|
||||
this.name = name == null ? jdbcType.getName() : name;
|
||||
this.precision = precision;
|
||||
this.scale = scale;
|
||||
this.alterNames = ImmutableList.copyOf(alterNames);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, String name, boolean precision, String... alterNames) {
|
||||
this(jdbcType, name, precision, false, alterNames);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, String name, boolean precision) {
|
||||
this(jdbcType, name, precision, false);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, String name, String... alterNames) {
|
||||
this(jdbcType, name, false, false, alterNames);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, boolean precision, boolean scale) {
|
||||
this(jdbcType, null, precision, scale);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType, boolean precision) {
|
||||
this(jdbcType, null, precision, false);
|
||||
}
|
||||
|
||||
private DialectType(@NotNull JDBCType jdbcType) {
|
||||
this(jdbcType, null, false, false);
|
||||
}
|
||||
|
||||
private String getNameResolved() {
|
||||
return name.replace(PLACEHOLDER_TYPE, jdbcType.getName());
|
||||
}
|
||||
|
||||
private String formatSql(Integer precision, Integer scale) {
|
||||
int idx = name.indexOf(PLACEHOLDER_TYPE);
|
||||
if (idx >= 0) {
|
||||
return name.substring(0, idx) + addSqlPrecisionScale(jdbcType.getName(), precision, scale)
|
||||
+ name.substring(idx + PLACEHOLDER_TYPE.length());
|
||||
} else {
|
||||
return addSqlPrecisionScale(name, precision, scale);
|
||||
}
|
||||
}
|
||||
|
||||
private String addSqlPrecisionScale(@NotNull String name, Integer precision, Integer scale) {
|
||||
if (!this.precision || precision == null) {
|
||||
return name;
|
||||
}
|
||||
String sql = name + '(' + precision;
|
||||
if (this.scale || scale != null) {
|
||||
sql = sql + ',' + scale;
|
||||
}
|
||||
sql += ')';
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service.database;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum SqlOperator {
|
||||
|
||||
EQ("="), //
|
||||
NEQ("<>"), //
|
||||
GTE(">="), //
|
||||
LTE("<="), //
|
||||
GT(">"), //
|
||||
LT("<"), //
|
||||
LIKE("LIKE") { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s %s", definition, getSymbol(), sqlGenerator.formatValue(columnType, "%" + values[0] + "%"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatPlaceholderImpl(String definition, String placeholder) {
|
||||
return format("%s %s CONCAT('%%', %s, '%%')", definition, getSymbol(), placeholder);
|
||||
}
|
||||
},
|
||||
NLIKE("NOT LIKE") { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s %s", definition, getSymbol(), sqlGenerator.formatValue(columnType, "%" + values[0] + "%"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatPlaceholderImpl(String definition, String placeholder) {
|
||||
return format("%s %s CONCAT('%%', %s, '%%')", definition, getSymbol(), placeholder);
|
||||
}
|
||||
},
|
||||
BTW("BETWEEN", 2) { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s %s AND %s", definition, getSymbol(), sqlGenerator.formatValue(columnType, values[0]),
|
||||
sqlGenerator.formatValue(columnType, values[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatPlaceholderImpl(String definition, String placeholder) {
|
||||
return format("%s %s %s AND %s", definition, getSymbol(), placeholder, placeholder);
|
||||
}
|
||||
},
|
||||
NBTW("NOT BETWEEN", 2) { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s %s AND %s", definition, getSymbol(), sqlGenerator.formatValue(columnType, values[0]),
|
||||
sqlGenerator.formatValue(columnType, values[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatPlaceholderImpl(String definition, String placeholder) {
|
||||
return format("%s %s %s AND %s", definition, getSymbol(), placeholder, placeholder);
|
||||
}
|
||||
},
|
||||
IN("IN", -1) { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s (%s)", definition, getSymbol(),
|
||||
Arrays.stream(values).map(e -> sqlGenerator.formatValue(columnType, e)).collect(Collectors.joining(", ")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaceholderSupported() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
NIN("NOT IN", -1) { //
|
||||
|
||||
@Override
|
||||
public String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return format("%s %s (%s)", definition, getSymbol(),
|
||||
Arrays.stream(values).map(e -> sqlGenerator.formatValue(columnType, e)).collect(Collectors.joining(", ")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaceholderSupported() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
NULL("IS NULL", 0), //
|
||||
NNULL("IS NOT NULL", 0), //
|
||||
;
|
||||
|
||||
private final String symbol;
|
||||
private final int paramCount;
|
||||
|
||||
SqlOperator(String symbol) {
|
||||
this(symbol, 1);
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return this == getDefault();
|
||||
}
|
||||
|
||||
public static SqlOperator getDefault() {
|
||||
return EQ;
|
||||
}
|
||||
|
||||
public boolean isListType() {
|
||||
return paramCount < 0;
|
||||
}
|
||||
|
||||
public String formatSql(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition, String alias,
|
||||
List<String> values) {
|
||||
return formatSql(sqlGenerator, columnType, definition, alias, values == null ? null : values.toArray(String[]::new));
|
||||
}
|
||||
|
||||
public String formatSql(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition, String alias,
|
||||
String... values) {
|
||||
validateValues(values);
|
||||
return formatImpl(sqlGenerator, columnType, sqlGenerator.alias(sqlGenerator.escape(definition), alias), values);
|
||||
}
|
||||
|
||||
protected String formatImpl(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, JdbcJavaType columnType, String definition,
|
||||
String... values) {
|
||||
return paramCount == 0 ? format("%s %s", definition, symbol)
|
||||
: format("%s %s %s", definition, symbol, sqlGenerator.formatValue(columnType, values[0]));
|
||||
}
|
||||
|
||||
public boolean isPlaceholderSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String formatPlaceholder(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, String definition, String alias) {
|
||||
return formatPlaceholder(sqlGenerator, definition, alias, "?");
|
||||
}
|
||||
|
||||
public String formatPlaceholder(@NotNull DatabaseSpecificSQLGenerator sqlGenerator, String definition, String alias,
|
||||
String placeholder) {
|
||||
return formatPlaceholderImpl(sqlGenerator.alias(sqlGenerator.escape(definition), alias), placeholder);
|
||||
}
|
||||
|
||||
protected String formatPlaceholderImpl(String definition, String placeholder) {
|
||||
if (!isPlaceholderSupported()) {
|
||||
throw new UnsupportedOperationException("Placeholder is not supported for this operator");
|
||||
}
|
||||
return paramCount == 0 ? format("%s %s", definition, symbol) : format("%s %s %s", definition, symbol, placeholder);
|
||||
}
|
||||
|
||||
public void validateValues(String... values) {
|
||||
if (values == null ? paramCount != 0 : (paramCount < 0 ? values.length < -paramCount : values.length != paramCount)) {
|
||||
throw new PlatformServiceUnavailableException("error.msg.database.operator.invalid",
|
||||
"Number of parameters " + Arrays.toString(values) + " must be " + Math.abs(paramCount) + " on " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateValues(List<?> values) {
|
||||
int size = values == null ? 0 : values.size();
|
||||
if (paramCount < 0 ? size < -paramCount : size != paramCount) {
|
||||
throw new PlatformServiceUnavailableException("error.msg.database.operator.invalid",
|
||||
"Number of parameters " + size + " must be " + Math.abs(paramCount) + " on " + this);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static SqlOperator forName(String name) {
|
||||
return name == null ? getDefault() : SqlOperator.valueOf(name.toUpperCase());
|
||||
}
|
||||
}
|
||||
+172
-246
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.dataqueries.data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a resultset column.
|
||||
@@ -29,9 +32,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityEx
|
||||
public final class ResultsetColumnHeaderData implements Serializable {
|
||||
|
||||
private final String columnName;
|
||||
private String columnType;
|
||||
private JdbcJavaType columnType;
|
||||
private final Long columnLength;
|
||||
private final String columnDisplayType;
|
||||
private final DisplayType columnDisplayType;
|
||||
private final boolean isColumnNullable;
|
||||
private final boolean isColumnPrimaryKey;
|
||||
private final boolean isColumnUnique;
|
||||
@@ -40,8 +43,7 @@ public final class ResultsetColumnHeaderData implements Serializable {
|
||||
private final List<ResultsetColumnValueData> columnValues;
|
||||
private final String columnCode;
|
||||
|
||||
public static ResultsetColumnHeaderData basic(final String columnName, final String columnType) {
|
||||
|
||||
public static ResultsetColumnHeaderData basic(final String columnName, final String columnType, DatabaseType dialect) {
|
||||
final Long columnLength = null;
|
||||
final boolean columnNullable = false;
|
||||
final boolean columnIsPrimaryKey = false;
|
||||
@@ -50,21 +52,20 @@ public final class ResultsetColumnHeaderData implements Serializable {
|
||||
final boolean columnIsUnique = false;
|
||||
final boolean columnIsIndexed = false;
|
||||
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues,
|
||||
columnCode, columnIsUnique, columnIsIndexed);
|
||||
columnCode, columnIsUnique, columnIsIndexed, dialect);
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData detailed(final String columnName, final String columnType, final Long columnLength,
|
||||
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues,
|
||||
final String columnCode, final boolean columnIsUnique, final boolean columnIsIndexed) {
|
||||
final String columnCode, final boolean columnIsUnique, final boolean columnIsIndexed, DatabaseType dialect) {
|
||||
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues,
|
||||
columnCode, columnIsUnique, columnIsIndexed);
|
||||
columnCode, columnIsUnique, columnIsIndexed, dialect);
|
||||
}
|
||||
|
||||
private ResultsetColumnHeaderData(final String columnName, final String columnType, final Long columnLength,
|
||||
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues,
|
||||
final String columnCode, final boolean columnIsUnique, final boolean columnIsIndexed) {
|
||||
private ResultsetColumnHeaderData(final String columnName, String columnType, final Long columnLength, final boolean columnNullable,
|
||||
final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues, final String columnCode,
|
||||
final boolean columnIsUnique, final boolean columnIsIndexed, DatabaseType dialect) {
|
||||
this.columnName = columnName;
|
||||
this.columnType = columnType;
|
||||
this.columnLength = columnLength;
|
||||
this.isColumnNullable = columnNullable;
|
||||
this.isColumnPrimaryKey = columnIsPrimaryKey;
|
||||
@@ -74,179 +75,20 @@ public final class ResultsetColumnHeaderData implements Serializable {
|
||||
this.isColumnIndexed = columnIsIndexed;
|
||||
|
||||
// Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
|
||||
adjustColumnTypes();
|
||||
this.columnType = JdbcJavaType.getByTypeName(dialect, adjustColumnType(columnType));
|
||||
|
||||
String displayType = null;
|
||||
if (this.columnCode == null) {
|
||||
if (isString()) {
|
||||
displayType = "STRING";
|
||||
} else if (isAnyInteger()) {
|
||||
if (isInteger()) {
|
||||
this.columnType = this.columnType.toUpperCase();
|
||||
}
|
||||
displayType = "INTEGER";
|
||||
} else if (isDate()) {
|
||||
displayType = "DATE";
|
||||
} else if (isDateTime()) {
|
||||
displayType = "DATETIME";
|
||||
} else if (isDecimal()) {
|
||||
displayType = "DECIMAL";
|
||||
} else if (isAnyText()) {
|
||||
displayType = "TEXT";
|
||||
} else if (isBit() || isBoolean()) {
|
||||
displayType = "BOOLEAN";
|
||||
} else {
|
||||
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type",
|
||||
"Invalid Lookup Type:" + this.columnType + " - Column Name: " + this.columnName);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (isInt() || isInteger()) {
|
||||
displayType = "CODELOOKUP";
|
||||
} else if (isVarchar()) {
|
||||
displayType = "CODEVALUE";
|
||||
} else {
|
||||
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type",
|
||||
"Invalid Lookup Type:" + this.columnType + " - Column Name: " + this.columnName);
|
||||
}
|
||||
}
|
||||
|
||||
this.columnDisplayType = displayType;
|
||||
}
|
||||
|
||||
private void adjustColumnTypes() {
|
||||
switch (this.columnType) {
|
||||
case "NEWDECIMAL":
|
||||
this.columnType = "DECIMAL";
|
||||
break;
|
||||
case "CLOB":
|
||||
case "ENUM":
|
||||
case "SET":
|
||||
this.columnType = "varchar";
|
||||
break;
|
||||
case "LONGLONG":
|
||||
this.columnType = "bigint";
|
||||
break;
|
||||
case "SHORT":
|
||||
this.columnType = "smallint";
|
||||
break;
|
||||
case "TINY":
|
||||
this.columnType = "tinyint";
|
||||
break;
|
||||
case "INT24":
|
||||
this.columnType = "int";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNamed(final String columnName) {
|
||||
return this.columnName.equalsIgnoreCase(columnName);
|
||||
}
|
||||
|
||||
private boolean isAnyText() {
|
||||
return isText() || isTinyText() || isMediumText() || isLongText();
|
||||
}
|
||||
|
||||
private boolean isText() {
|
||||
return "text".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isTinyText() {
|
||||
return "tinytext".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isMediumText() {
|
||||
return "mediumtext".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isLongText() {
|
||||
return "longtext".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isDecimal() {
|
||||
return "decimal".equalsIgnoreCase(this.columnType) || "NEWDECIMAL".equalsIgnoreCase(this.columnType)
|
||||
|| "numeric".equalsIgnoreCase(this.columnType);
|
||||
// Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
|
||||
}
|
||||
|
||||
private boolean isDate() {
|
||||
return "date".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isDateTime() {
|
||||
return "datetime".equalsIgnoreCase(this.columnType) || "timestamp".equalsIgnoreCase(this.columnType)
|
||||
|| "timestamp without time zone".equalsIgnoreCase(this.columnType) || "timestamptz".equalsIgnoreCase(this.columnType)
|
||||
|| "timestamp with time zone".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return isVarchar() || isChar();
|
||||
}
|
||||
|
||||
private boolean isChar() {
|
||||
return "char".equalsIgnoreCase(this.columnType) || "CHARACTER VARYING".equalsIgnoreCase(this.columnType)
|
||||
|| "bpchar".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isVarchar() {
|
||||
return "varchar".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isAnyInteger() {
|
||||
return isInt() || isInteger() || isSmallInt() || isTinyInt() || isMediumInt() || isBigInt() || isLong() || isSerial();
|
||||
}
|
||||
|
||||
private boolean isSerial() {
|
||||
return "SERIAL".equalsIgnoreCase(this.columnType) || "SERIAL4".equalsIgnoreCase(this.columnType)
|
||||
|| "SERIAL8".equalsIgnoreCase(this.columnType) || "SMALLSERIAL".equalsIgnoreCase(this.columnType)
|
||||
|| "BIGSERIAL".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isInt() {
|
||||
return "int".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isInteger() {
|
||||
return "integer".equalsIgnoreCase(this.columnType) || "int2".equalsIgnoreCase(this.columnType)
|
||||
|| "int4".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isSmallInt() {
|
||||
return "smallint".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isTinyInt() {
|
||||
return "tinyint".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isMediumInt() {
|
||||
return "mediumint".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isBigInt() {
|
||||
return "bigint".equalsIgnoreCase(this.columnType) || "int8".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isLong() {
|
||||
return "LONG".equalsIgnoreCase(this.columnType) || "LONGLONG".equalsIgnoreCase(this.columnType);
|
||||
// Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
|
||||
}
|
||||
|
||||
private boolean isBit() {
|
||||
return "bit".equalsIgnoreCase(this.columnType);
|
||||
}
|
||||
|
||||
private boolean isBoolean() {
|
||||
return "boolean".equalsIgnoreCase(this.columnType) || "bool".equalsIgnoreCase(this.columnType);
|
||||
this.columnDisplayType = calcDisplayType();
|
||||
}
|
||||
|
||||
public String getColumnName() {
|
||||
return this.columnName;
|
||||
}
|
||||
|
||||
public String getColumnType() {
|
||||
public boolean isNamed(final String columnName) {
|
||||
return this.columnName.equalsIgnoreCase(columnName);
|
||||
}
|
||||
|
||||
public JdbcJavaType getColumnType() {
|
||||
return this.columnType;
|
||||
}
|
||||
|
||||
@@ -270,78 +112,10 @@ public final class ResultsetColumnHeaderData implements Serializable {
|
||||
return isColumnIndexed;
|
||||
}
|
||||
|
||||
public String getColumnDisplayType() {
|
||||
public DisplayType getColumnDisplayType() {
|
||||
return this.columnDisplayType;
|
||||
}
|
||||
|
||||
public boolean isDateDisplayType() {
|
||||
return "DATE".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isDateTimeDisplayType() {
|
||||
return "DATETIME".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isIntegerDisplayType() {
|
||||
return "INTEGER".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isDecimalDisplayType() {
|
||||
return "DECIMAL".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isBooleanDisplayType() {
|
||||
return "BOOLEAN".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isCodeValueDisplayType() {
|
||||
return "CODEVALUE".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isCodeLookupDisplayType() {
|
||||
return "CODELOOKUP".equalsIgnoreCase(this.columnDisplayType);
|
||||
}
|
||||
|
||||
public boolean isMandatory() {
|
||||
return !isOptional();
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return this.isColumnNullable;
|
||||
}
|
||||
|
||||
public boolean hasColumnValues() {
|
||||
return !this.columnValues.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isColumnValueAllowed(final String match) {
|
||||
boolean allowed = false;
|
||||
for (final ResultsetColumnValueData allowedValue : this.columnValues) {
|
||||
if (allowedValue.matches(match)) {
|
||||
allowed = true;
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public boolean isColumnValueNotAllowed(final String match) {
|
||||
return !isColumnValueAllowed(match);
|
||||
}
|
||||
|
||||
public boolean isColumnCodeNotAllowed(final Integer match) {
|
||||
return !isColumnCodeAllowed(match);
|
||||
}
|
||||
|
||||
public boolean isColumnCodeAllowed(final Integer match) {
|
||||
boolean allowed = false;
|
||||
for (final ResultsetColumnValueData allowedValue : this.columnValues) {
|
||||
if (allowedValue.codeMatches(match)) {
|
||||
allowed = true;
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public String getColumnCode() {
|
||||
return this.columnCode;
|
||||
}
|
||||
@@ -349,4 +123,156 @@ public final class ResultsetColumnHeaderData implements Serializable {
|
||||
public List<ResultsetColumnValueData> getColumnValues() {
|
||||
return this.columnValues;
|
||||
}
|
||||
|
||||
public boolean isDateDisplayType() {
|
||||
return columnDisplayType == DisplayType.DATE;
|
||||
}
|
||||
|
||||
public boolean isDateTimeDisplayType() {
|
||||
return columnDisplayType == DisplayType.DATETIME;
|
||||
}
|
||||
|
||||
public boolean isTimeDisplayType() {
|
||||
return columnDisplayType == DisplayType.TIME;
|
||||
}
|
||||
|
||||
public boolean isIntegerDisplayType() {
|
||||
return columnDisplayType == DisplayType.INTEGER;
|
||||
}
|
||||
|
||||
public boolean isDecimalDisplayType() {
|
||||
return columnDisplayType == DisplayType.DECIMAL;
|
||||
}
|
||||
|
||||
public boolean isStringDisplayType() {
|
||||
return columnDisplayType == DisplayType.STRING;
|
||||
}
|
||||
|
||||
public boolean isTextDisplayType() {
|
||||
return columnDisplayType == DisplayType.TEXT;
|
||||
}
|
||||
|
||||
public boolean isBooleanDisplayType() {
|
||||
return columnDisplayType == DisplayType.BOOLEAN;
|
||||
}
|
||||
|
||||
public boolean isCodeValueDisplayType() {
|
||||
return columnDisplayType == DisplayType.CODEVALUE;
|
||||
}
|
||||
|
||||
public boolean isCodeLookupDisplayType() {
|
||||
return columnDisplayType == DisplayType.CODELOOKUP;
|
||||
}
|
||||
|
||||
public boolean isMandatory() {
|
||||
return !this.isColumnNullable;
|
||||
}
|
||||
|
||||
public boolean hasColumnValues() {
|
||||
return columnValues != null && !columnValues.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isColumnValueAllowed(final String match) {
|
||||
for (final ResultsetColumnValueData allowedValue : this.columnValues) {
|
||||
if (allowedValue.matches(match)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isColumnCodeAllowed(final Integer match) {
|
||||
for (final ResultsetColumnValueData allowedValue : this.columnValues) {
|
||||
if (allowedValue.codeMatches(match)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasPrecision(@NotNull DatabaseType dialect) {
|
||||
return columnType.hasPrecision(dialect);
|
||||
}
|
||||
|
||||
// --- Calculation ---
|
||||
|
||||
private String adjustColumnType(String type) {
|
||||
type = type.toUpperCase();
|
||||
switch (type) {
|
||||
case "CLOB":
|
||||
case "ENUM":
|
||||
case "SET":
|
||||
return "VARCHAR";
|
||||
case "NEWDECIMAL":
|
||||
return "DECIMAL";
|
||||
case "LONGLONG":
|
||||
return "BIGINT";
|
||||
case "SHORT":
|
||||
return "SMALLINT";
|
||||
case "TINY":
|
||||
return "TINYINT";
|
||||
case "INT24":
|
||||
return "INT";
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DisplayType calcDisplayType() {
|
||||
DisplayType displayType = null;
|
||||
if (this.columnCode == null) {
|
||||
displayType = calcColumnDisplayType(columnType);
|
||||
} else {
|
||||
if (columnType.isIntegerType()) {
|
||||
displayType = DisplayType.CODELOOKUP;
|
||||
} else if (columnType.isVarcharType()) {
|
||||
displayType = DisplayType.CODEVALUE;
|
||||
}
|
||||
|
||||
}
|
||||
if (displayType == null) {
|
||||
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type",
|
||||
"Invalid Lookup Type:" + this.columnType + " - Column Name: " + this.columnName);
|
||||
}
|
||||
return displayType;
|
||||
}
|
||||
|
||||
public static DisplayType calcColumnDisplayType(JdbcJavaType columnType) {
|
||||
if (columnType.isTextType()) {
|
||||
return DisplayType.TEXT;
|
||||
}
|
||||
if (columnType.isStringType()) {
|
||||
return DisplayType.STRING;
|
||||
}
|
||||
if (columnType.isAnyIntegerType()) {
|
||||
return DisplayType.INTEGER;
|
||||
}
|
||||
if (columnType.isAnyFloatType()) {
|
||||
return DisplayType.FLOAT;
|
||||
}
|
||||
if (columnType.isDecimalType()) { // Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
|
||||
return DisplayType.DECIMAL;
|
||||
}
|
||||
if (columnType.isDateType()) {
|
||||
return DisplayType.DATE;
|
||||
}
|
||||
if (columnType.isDateTimeType()) {
|
||||
return DisplayType.DATETIME;
|
||||
}
|
||||
if (columnType.isTimeType()) {
|
||||
return DisplayType.TIME;
|
||||
}
|
||||
if (columnType.isBooleanType()) {
|
||||
return DisplayType.BOOLEAN;
|
||||
}
|
||||
if (columnType.isBinaryType()) {
|
||||
return DisplayType.BINARY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public enum DisplayType {
|
||||
TEXT, STRING, INTEGER, FLOAT, DECIMAL, DATE, TIME, DATETIME, BOOLEAN, BINARY, CODELOOKUP, CODEVALUE,;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,9 +26,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.cob.loan.LoanCOBBusinessStep;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAccountSnapshotBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.investor.config.InvestorModuleIsEnabledCondition;
|
||||
import org.apache.fineract.investor.data.ExternalTransferStatus;
|
||||
import org.apache.fineract.investor.data.ExternalTransferSubStatus;
|
||||
|
||||
+1
-1
@@ -34,9 +34,9 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAccountSnapshotBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.investor.data.ExternalTransferStatus;
|
||||
import org.apache.fineract.investor.data.ExternalTransferSubStatus;
|
||||
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
|
||||
|
||||
+1
-1
@@ -71,8 +71,8 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
|
||||
import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
|
||||
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.security.service.RandomPasswordGenerator;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
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.LoanInstallmentChargeData;
|
||||
|
||||
+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,
|
||||
|
||||
+1
-1
@@ -34,6 +34,7 @@ import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
@@ -46,7 +47,6 @@ import org.apache.fineract.interoperation.domain.InteropActionState;
|
||||
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
|
||||
import org.apache.fineract.interoperation.domain.InteropTransactionRole;
|
||||
import org.apache.fineract.interoperation.util.InteropUtil;
|
||||
import org.apache.fineract.interoperation.util.MathUtil;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
Reference in New Issue
Block a user