FINERACT-1757: Uppercase letters in the datatable name is failing on postgres
This commit is contained in:
committed by
Adam Saghy
parent
5f713be7ec
commit
6f2b0198a2
+19
-3
@@ -204,17 +204,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<JsonObject> queryDataTable(String datatable, String columnFilter, String valueFilter, String resultColumns) {
|
public List<JsonObject> queryDataTable(String datatable, final String columnFilter, String valueFilter, String resultColumns) {
|
||||||
Arrays.asList(datatable, columnFilter, valueFilter, resultColumns).forEach(SQLInjectionValidator::validateDynamicQuery);
|
Arrays.asList(datatable, columnFilter, valueFilter, resultColumns).forEach(SQLInjectionValidator::validateDynamicQuery);
|
||||||
|
|
||||||
List<ResultsetColumnHeaderData> resultsetColumnHeaderData = genericDataService.fillResultsetColumnHeaders(datatable);
|
List<ResultsetColumnHeaderData> resultsetColumnHeaderData = genericDataService.fillResultsetColumnHeaders(datatable);
|
||||||
validateRequestParams(columnFilter, valueFilter, resultColumns, resultsetColumnHeaderData);
|
validateRequestParams(columnFilter, valueFilter, resultColumns, resultsetColumnHeaderData);
|
||||||
|
String filterColumnType = resultsetColumnHeaderData.stream().filter(column -> Objects.equals(columnFilter, column.getColumnName()))
|
||||||
|
.findFirst().map(ResultsetColumnHeaderData::getColumnType).orElse(columnFilter + " does not exist in datatable");
|
||||||
|
|
||||||
String sql = "select " + resultColumns + " from " + datatable + " where " + columnFilter + " = ?";
|
String sql = "select " + resultColumns + " from " + datatable + " where " + columnFilter + " = ?";
|
||||||
SqlRowSet rowSet = null;
|
SqlRowSet rowSet = null;
|
||||||
String filterColumnType = resultsetColumnHeaderData.stream().filter(column -> Objects.equals(columnFilter, column.getColumnName()))
|
|
||||||
.findFirst().map(ResultsetColumnHeaderData::getColumnType).orElse(columnFilter + " does not exist in datatable");
|
|
||||||
if (databaseTypeResolver.isPostgreSQL()) {
|
if (databaseTypeResolver.isPostgreSQL()) {
|
||||||
|
sql = "select " + escapeFieldNames(resultColumns) + " from " + sqlGenerator.escape(datatable) + " where "
|
||||||
|
+ escapeFieldNames(columnFilter) + " = ?";
|
||||||
rowSet = callFilteredPgSql(sql, valueFilter, filterColumnType);
|
rowSet = callFilteredPgSql(sql, valueFilter, filterColumnType);
|
||||||
} else if (databaseTypeResolver.isMySQL()) {
|
} else if (databaseTypeResolver.isMySQL()) {
|
||||||
rowSet = callFilteredMysql(sql, valueFilter, filterColumnType);
|
rowSet = callFilteredMysql(sql, valueFilter, filterColumnType);
|
||||||
@@ -234,6 +236,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
private void extractResults(SqlRowSet rowSet, String[] resultColumnNames, List<JsonObject> results) {
|
private void extractResults(SqlRowSet rowSet, String[] resultColumnNames, List<JsonObject> results) {
|
||||||
JsonObject json = new JsonObject();
|
JsonObject json = new JsonObject();
|
||||||
for (String rcn : resultColumnNames) {
|
for (String rcn : resultColumnNames) {
|
||||||
|
rcn = rcn.replaceAll("\"", "");
|
||||||
Object rowValue = rowSet.getObject(rcn);
|
Object rowValue = rowSet.getObject(rcn);
|
||||||
if (rowValue != null) {
|
if (rowValue != null) {
|
||||||
if (rowValue instanceof Character) {
|
if (rowValue instanceof Character) {
|
||||||
@@ -332,6 +335,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
|||||||
return jdbcTemplate.queryForRowSet(sql, new Object[] { finalValueFilter }, argType);
|
return jdbcTemplate.queryForRowSet(sql, new Object[] { finalValueFilter }, argType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String escapeFieldNames(final String inputFields) {
|
||||||
|
final String delimiter = ",";
|
||||||
|
if (StringUtils.isBlank(inputFields)) {
|
||||||
|
return inputFields;
|
||||||
|
}
|
||||||
|
final String[] fieldList = StringUtils.split(inputFields, delimiter);
|
||||||
|
final String[] outputFields = new String[fieldList.length];
|
||||||
|
for (int i = 0; i < fieldList.length; i++) {
|
||||||
|
outputFields[i] = sqlGenerator.escape(fieldList[i].trim());
|
||||||
|
}
|
||||||
|
return String.join(",", outputFields);
|
||||||
|
}
|
||||||
|
|
||||||
private static void validateRequestParams(String columnFilter, String valueFilter, String resultColumns,
|
private static void validateRequestParams(String columnFilter, String valueFilter, String resultColumns,
|
||||||
List<ResultsetColumnHeaderData> resultsetColumnHeaderData) {
|
List<ResultsetColumnHeaderData> resultsetColumnHeaderData) {
|
||||||
List<ApiParameterError> paramErrors = new ArrayList<>();
|
List<ApiParameterError> paramErrors = new ArrayList<>();
|
||||||
|
|||||||
+5
@@ -27,6 +27,7 @@ import com.google.gson.JsonObject;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
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.DatabaseTypeResolver;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionException;
|
import org.apache.fineract.infrastructure.security.utils.SQLInjectionException;
|
||||||
@@ -52,6 +53,9 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private DatabaseTypeResolver databaseTypeResolver;
|
private DatabaseTypeResolver databaseTypeResolver;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private ReadWriteNonCoreDataServiceImpl underTest;
|
private ReadWriteNonCoreDataServiceImpl underTest;
|
||||||
|
|
||||||
@@ -81,6 +85,7 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
|||||||
.thenReturn(sqlRS);
|
.thenReturn(sqlRS);
|
||||||
when(sqlRS.next()).thenReturn(true).thenReturn(false);
|
when(sqlRS.next()).thenReturn(true).thenReturn(false);
|
||||||
when(sqlRS.getObject(ArgumentMatchers.anyString())).thenReturn("value1").thenReturn("value2");
|
when(sqlRS.getObject(ArgumentMatchers.anyString())).thenReturn("value1").thenReturn("value2");
|
||||||
|
when(sqlGenerator.escape(ArgumentMatchers.anyString())).thenReturn("rc1").thenReturn("rc2").thenReturn("table").thenReturn("cf1");
|
||||||
when(databaseTypeResolver.isPostgreSQL()).thenReturn(true);
|
when(databaseTypeResolver.isPostgreSQL()).thenReturn(true);
|
||||||
|
|
||||||
ResultsetColumnHeaderData cf1 = ResultsetColumnHeaderData.detailed("cf1", "text", 10L, false, false, null, null, false, false);
|
ResultsetColumnHeaderData cf1 = ResultsetColumnHeaderData.detailed("cf1", "text", 10L, false, false, null, null, false, false);
|
||||||
|
|||||||
+6
@@ -158,6 +158,12 @@ public class DatatableHelper extends IntegrationTest {
|
|||||||
return Utils.performServerDelete(this.requestSpec, this.responseSpec, deleteEntryUrl, jsonAttributeToGetBack);
|
return Utils.performServerDelete(this.requestSpec, this.responseSpec, deleteEntryUrl, jsonAttributeToGetBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String runDatatableQuery(final String datatableName, final String columnFilter, final String valueFilter,
|
||||||
|
final String resultColumns) {
|
||||||
|
return Utils.performServerGet(this.requestSpec, this.responseSpec, DATATABLE_URL + "/" + datatableName + "/query" + "?columnFilter="
|
||||||
|
+ columnFilter + "&valueFilter=" + valueFilter + "&resultColumns=" + resultColumns + "&" + Utils.TENANT_IDENTIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
public static void verifyDatatableCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
public static void verifyDatatableCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
final String generatedDatatableName) {
|
final String generatedDatatableName) {
|
||||||
LOG.info("------------------------------CHECK DATATABLE DETAILS------------------------------------\n");
|
LOG.info("------------------------------CHECK DATATABLE DETAILS------------------------------------\n");
|
||||||
|
|||||||
+102
@@ -309,6 +309,108 @@ public class DatatableIntegrationTest extends IntegrationTest {
|
|||||||
assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING THE DATATABLE");
|
assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING THE DATATABLE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateCreateReadDeleteDatatableWithCaseSensitive() throws ParseException {
|
||||||
|
|
||||||
|
// creating datatable for client entity
|
||||||
|
final HashMap<String, Object> columnMap = new HashMap<>();
|
||||||
|
final List<HashMap<String, Object>> datatableColumnsList = new ArrayList<>();
|
||||||
|
columnMap.put("datatableName", Utils.uniqueRandomStringGenerator(CLIENT_APP_TABLE_NAME + "_", 5));
|
||||||
|
columnMap.put("apptableName", CLIENT_APP_TABLE_NAME);
|
||||||
|
columnMap.put("entitySubType", "PERSON");
|
||||||
|
columnMap.put("multiRow", false);
|
||||||
|
String itsADate = "itsADate";
|
||||||
|
String itsADecimal = "itsADecimal";
|
||||||
|
String itsAString = "itsAString";
|
||||||
|
String dateFormat = "dateFormat";
|
||||||
|
|
||||||
|
DatatableHelper.addDatatableColumns(datatableColumnsList, itsADate, "Date", true, null, null);
|
||||||
|
DatatableHelper.addDatatableColumns(datatableColumnsList, itsADecimal, "Decimal", true, null, null);
|
||||||
|
DatatableHelper.addDatatableColumns(datatableColumnsList, itsAString, "String", true, 10, null);
|
||||||
|
columnMap.put("columns", datatableColumnsList);
|
||||||
|
String datatabelRequestJsonString = new Gson().toJson(columnMap);
|
||||||
|
LOG.info("map : {}", datatabelRequestJsonString);
|
||||||
|
|
||||||
|
HashMap<String, Object> datatableResponse = this.datatableHelper.createDatatable(datatabelRequestJsonString, "");
|
||||||
|
String datatableName = (String) datatableResponse.get("resourceIdentifier");
|
||||||
|
DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, this.responseSpec, datatableName);
|
||||||
|
|
||||||
|
// creating client with datatables
|
||||||
|
final Integer clientID = ClientHelper.createClientAsPerson(requestSpec, responseSpec);
|
||||||
|
|
||||||
|
// creating new client datatable entry
|
||||||
|
final boolean genericResultSet = true;
|
||||||
|
|
||||||
|
final HashMap<String, Object> datatableEntryMap = new HashMap<>();
|
||||||
|
datatableEntryMap.put(itsADate, Utils.randomDateGenerator("yyyy-MM-dd"));
|
||||||
|
datatableEntryMap.put(itsADecimal, Utils.randomDecimalGenerator(4, 3));
|
||||||
|
datatableEntryMap.put(itsAString, Utils.randomStringGenerator("", 8));
|
||||||
|
datatableEntryMap.put("locale", "en");
|
||||||
|
datatableEntryMap.put(dateFormat, "yyyy-MM-dd");
|
||||||
|
|
||||||
|
String datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
|
||||||
|
LOG.info("map : {}", datatabelEntryRequestJsonString);
|
||||||
|
|
||||||
|
HashMap<String, Object> datatableEntryResponse = this.datatableHelper.createDatatableEntry(datatableName, clientID,
|
||||||
|
genericResultSet, datatabelEntryRequestJsonString);
|
||||||
|
assertNotNull(datatableEntryResponse.get("resourceId"), "ERROR IN CREATING THE ENTITY DATATABLE RECORD");
|
||||||
|
|
||||||
|
// Read the Datatable entry generated with genericResultSet in true (default)
|
||||||
|
final HashMap<String, Object> items = this.datatableHelper.readDatatableEntry(datatableName, clientID, genericResultSet,
|
||||||
|
(Integer) datatableEntryResponse.get("resourceId"), "");
|
||||||
|
assertNotNull(items);
|
||||||
|
assertEquals(1, ((List) items.get("data")).size());
|
||||||
|
|
||||||
|
assertEquals("client_id", ((Map) ((List) items.get("columnHeaders")).get(0)).get("columnName"));
|
||||||
|
assertEquals(clientID, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(0));
|
||||||
|
|
||||||
|
assertEquals(itsADate, ((Map) ((List) items.get("columnHeaders")).get(1)).get("columnName"));
|
||||||
|
assertEquals(datatableEntryMap.get(itsADate),
|
||||||
|
Utils.arrayDateToString((List) ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(1)));
|
||||||
|
|
||||||
|
assertEquals(itsADecimal, ((Map) ((List) items.get("columnHeaders")).get(2)).get("columnName"));
|
||||||
|
assertEquals(datatableEntryMap.get(itsADecimal), ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(2));
|
||||||
|
|
||||||
|
assertEquals(itsAString, ((Map) ((List) items.get("columnHeaders")).get(3)).get("columnName"));
|
||||||
|
assertEquals(datatableEntryMap.get(itsAString), ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(3));
|
||||||
|
|
||||||
|
// Update datatable entry
|
||||||
|
final String randomValue = Utils.randomStringGenerator("", 8);
|
||||||
|
datatableEntryMap.put(itsADate, Utils.randomDateGenerator("yyyy-MM-dd"));
|
||||||
|
datatableEntryMap.put(itsADecimal, Utils.randomDecimalGenerator(4, 3));
|
||||||
|
datatableEntryMap.put(itsAString, randomValue);
|
||||||
|
|
||||||
|
datatableEntryMap.put("locale", "en");
|
||||||
|
datatableEntryMap.put(dateFormat, "yyyy-MM-dd");
|
||||||
|
|
||||||
|
datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap);
|
||||||
|
LOG.info("map : {}", datatabelEntryRequestJsonString);
|
||||||
|
|
||||||
|
HashMap<String, Object> updatedDatatableEntryResponse = this.datatableHelper.updateDatatableEntry(datatableName, clientID, false,
|
||||||
|
datatabelEntryRequestJsonString);
|
||||||
|
|
||||||
|
assertEquals(clientID, updatedDatatableEntryResponse.get("clientId"));
|
||||||
|
|
||||||
|
assertEquals(datatableEntryMap.get(itsADate),
|
||||||
|
Utils.arrayDateToString((List) ((Map) updatedDatatableEntryResponse.get("changes")).get(itsADate)));
|
||||||
|
assertEquals(datatableEntryMap.get(itsADecimal), ((Map) updatedDatatableEntryResponse.get("changes")).get(itsADecimal));
|
||||||
|
assertEquals(datatableEntryMap.get(itsAString), ((Map) updatedDatatableEntryResponse.get("changes")).get(itsAString));
|
||||||
|
|
||||||
|
// Read the datatable with a query
|
||||||
|
LOG.info("query in {} for value : {}", itsAString, randomValue);
|
||||||
|
final String queryResult = this.datatableHelper.runDatatableQuery(datatableName, itsAString, randomValue, "client_id,itsADecimal");
|
||||||
|
assertNotNull(queryResult);
|
||||||
|
LOG.info("query result : {}", queryResult);
|
||||||
|
|
||||||
|
// deleting datatable entries
|
||||||
|
Integer appTableId = this.datatableHelper.deleteDatatableEntries(datatableName, clientID, "clientId");
|
||||||
|
assertEquals(clientID, appTableId, "ERROR IN DELETING THE DATATABLE ENTRIES");
|
||||||
|
|
||||||
|
// deleting the datatable
|
||||||
|
String deletedDataTableName = this.datatableHelper.deleteDatatable(datatableName);
|
||||||
|
assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING THE DATATABLE");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateInsertNullValues() {
|
public void validateInsertNullValues() {
|
||||||
// Fetch / Create TST code
|
// Fetch / Create TST code
|
||||||
|
|||||||
Reference in New Issue
Block a user