udpated api json parsing (lenient mode); add escaping table names in rdbms

This commit is contained in:
2022-10-25 10:47:06 -05:00
parent dae803f709
commit 8ffc1c1a63
8 changed files with 120 additions and 45 deletions

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.utils;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -230,63 +231,110 @@ public class JsonUtils
** Convert a json object into a QRecord ** Convert a json object into a QRecord
** **
*******************************************************************************/ *******************************************************************************/
public static QRecord parseQRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields) public static QRecord parseQRecordStrict(JSONObject jsonObject, Map<String, QFieldMetaData> fields)
{
return (parseQRecord(jsonObject, fields, true));
}
/*******************************************************************************
** Convert a json object into a QRecord
**
*******************************************************************************/
public static QRecord parseQRecordLenient(JSONObject jsonObject, Map<String, QFieldMetaData> fields)
{
return (parseQRecord(jsonObject, fields, false));
}
/*******************************************************************************
** Convert a json object into a QRecord
**
*******************************************************************************/
private static QRecord parseQRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields, boolean strict)
{ {
QRecord record = new QRecord(); QRecord record = new QRecord();
FIELDS_LOOP: FIELDS_LOOP:
for(String fieldName : fields.keySet()) for(String fieldName : fields.keySet())
{ {
QFieldMetaData metaData = fields.get(fieldName); String originalBackendName = null;
String backendName = metaData.getBackendName() != null ? metaData.getBackendName() : fieldName; try
/////////////////////////////////////////////////////////////////////////////////////////////////
// if the field backend name has dots in it, interpret that to mean traversal down sub-objects //
/////////////////////////////////////////////////////////////////////////////////////////////////
JSONObject jsonObjectToUse = jsonObject;
if(backendName.contains("."))
{ {
ArrayList<String> levels = new ArrayList<>(List.of(backendName.split("\\."))); QFieldMetaData metaData = fields.get(fieldName);
backendName = levels.remove(levels.size() - 1); String backendName = metaData.getBackendName() != null ? metaData.getBackendName() : fieldName;
originalBackendName = backendName;
for(String level : levels) /////////////////////////////////////////////////////////////////////////////////////////////////
// if the field backend name has dots in it, interpret that to mean traversal down sub-objects //
/////////////////////////////////////////////////////////////////////////////////////////////////
JSONObject jsonObjectToUse = jsonObject;
if(backendName.contains("."))
{ {
try ArrayList<String> levels = new ArrayList<>(List.of(backendName.split("\\.")));
backendName = levels.remove(levels.size() - 1);
for(String level : levels)
{ {
jsonObjectToUse = jsonObjectToUse.optJSONObject(level); try
if(jsonObjectToUse == null) {
jsonObjectToUse = jsonObjectToUse.optJSONObject(level);
if(jsonObjectToUse == null)
{
continue FIELDS_LOOP;
}
}
catch(Exception e)
{ {
continue FIELDS_LOOP; continue FIELDS_LOOP;
} }
} }
catch(Exception e)
{
continue FIELDS_LOOP;
}
} }
}
if(jsonObjectToUse.isNull(backendName)) if(jsonObjectToUse.isNull(backendName))
{
record.setValue(fieldName, null);
continue;
}
switch(metaData.getType())
{
case INTEGER -> record.setValue(fieldName, jsonObjectToUse.optInt(backendName));
case DECIMAL -> record.setValue(fieldName, jsonObjectToUse.optBigDecimal(backendName, null));
case BOOLEAN -> record.setValue(fieldName, jsonObjectToUse.optBoolean(backendName));
case DATE_TIME ->
{ {
String dateTimeString = jsonObjectToUse.optString(backendName); record.setValue(fieldName, null);
if(StringUtils.hasContent(dateTimeString)) continue;
{ }
Instant instant = ValueUtils.getValueAsInstant(dateTimeString);
record.setValue(fieldName, instant); switch(metaData.getType())
} {
case INTEGER -> record.setValue(fieldName, jsonObjectToUse.optInt(backendName));
case DECIMAL -> record.setValue(fieldName, jsonObjectToUse.optBigDecimal(backendName, null));
case BOOLEAN -> record.setValue(fieldName, jsonObjectToUse.optBoolean(backendName));
case DATE_TIME ->
{
String dateTimeString = jsonObjectToUse.optString(backendName);
if(StringUtils.hasContent(dateTimeString))
{
Instant instant = ValueUtils.getValueAsInstant(dateTimeString);
record.setValue(fieldName, instant);
}
}
case DATE ->
{
String dateString = jsonObjectToUse.optString(backendName);
if(StringUtils.hasContent(dateString))
{
LocalDate localDate = ValueUtils.getValueAsLocalDate(dateString);
record.setValue(fieldName, localDate);
}
}
default -> record.setValue(fieldName, jsonObjectToUse.optString(backendName));
}
}
catch(Exception e)
{
if(strict)
{
throw e;
}
else
{
LOG.debug("Caught exception parsing field [" + fieldName + "] as [" + originalBackendName + "]", e);
} }
default -> record.setValue(fieldName, jsonObjectToUse.optString(backendName));
} }
} }

View File

@ -263,7 +263,7 @@ public class BaseAPIActionUtil
*******************************************************************************/ *******************************************************************************/
protected QRecord jsonObjectToRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields) throws IOException protected QRecord jsonObjectToRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields) throws IOException
{ {
QRecord record = JsonUtils.parseQRecord(jsonObject, fields); QRecord record = JsonUtils.parseQRecordLenient(jsonObject, fields);
return (record); return (record);
} }
@ -277,6 +277,11 @@ public class BaseAPIActionUtil
int statusCode = response.getStatusLine().getStatusCode(); int statusCode = response.getStatusLine().getStatusCode();
System.out.println(statusCode); System.out.println(statusCode);
if(statusCode >= 400)
{
handleGetResponseError(table, response);
}
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
String resultString = EntityUtils.toString(entity); String resultString = EntityUtils.toString(entity);
@ -318,6 +323,18 @@ public class BaseAPIActionUtil
/*******************************************************************************
**
*******************************************************************************/
private void handleGetResponseError(QTableMetaData table, HttpResponse response) throws IOException
{
HttpEntity entity = response.getEntity();
String resultString = EntityUtils.toString(entity);
throw new IOException("Error performing query: " + resultString);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -401,4 +401,14 @@ public abstract class AbstractRDBMSAction implements QActionInterface
} }
} }
/*******************************************************************************
**
*******************************************************************************/
protected String escapeIdentifier(String id)
{
return ("`" + id + "`");
}
} }

View File

@ -57,7 +57,7 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
QTableMetaData table = countInput.getTable(); QTableMetaData table = countInput.getTable();
String tableName = getTableName(table); String tableName = getTableName(table);
String sql = "SELECT count(*) as record_count FROM " + tableName; String sql = "SELECT count(*) as record_count FROM " + escapeIdentifier(tableName);
QQueryFilter filter = countInput.getFilter(); QQueryFilter filter = countInput.getFilter();
List<Serializable> params = new ArrayList<>(); List<Serializable> params = new ArrayList<>();

View File

@ -186,7 +186,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
// todo sql customization - can edit sql and/or param list? // todo sql customization - can edit sql and/or param list?
String sql = "DELETE FROM " String sql = "DELETE FROM "
+ tableName + escapeIdentifier(tableName)
+ " WHERE " + " WHERE "
+ primaryKeyName + " = ?"; + primaryKeyName + " = ?";

View File

@ -109,7 +109,7 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
{ {
for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE)) for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE))
{ {
String tableName = getTableName(table); String tableName = escapeIdentifier(getTableName(table));
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
List<Object> params = new ArrayList<>(); List<Object> params = new ArrayList<>();
int recordIndex = 0; int recordIndex = 0;

View File

@ -72,7 +72,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
.map(this::getColumnName) .map(this::getColumnName)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
String sql = "SELECT " + columns + " FROM " + tableName; String sql = "SELECT " + columns + " FROM " + escapeIdentifier(tableName);
QQueryFilter filter = queryInput.getFilter(); QQueryFilter filter = queryInput.getFilter();
List<Serializable> params = new ArrayList<>(); List<Serializable> params = new ArrayList<>();

View File

@ -223,7 +223,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
.map(f -> this.getColumnName(table.getField(f)) + " = ?") .map(f -> this.getColumnName(table.getField(f)) + " = ?")
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
String tableName = getTableName(table); String tableName = escapeIdentifier(getTableName(table));
return ("UPDATE " + tableName return ("UPDATE " + tableName
+ " SET " + columns + " SET " + columns
+ " WHERE " + getColumnName(table.getField(table.getPrimaryKeyField())) + " "); + " WHERE " + getColumnName(table.getField(table.getPrimaryKeyField())) + " ");