From 8ffc1c1a63b6a809fb42528dbaebfaee51d16b5e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 25 Oct 2022 10:47:06 -0500 Subject: [PATCH] udpated api json parsing (lenient mode); add escaping table names in rdbms --- .../qqq/backend/core/utils/JsonUtils.java | 126 ++++++++++++------ .../module/api/actions/BaseAPIActionUtil.java | 19 ++- .../rdbms/actions/AbstractRDBMSAction.java | 10 ++ .../rdbms/actions/RDBMSCountAction.java | 2 +- .../rdbms/actions/RDBMSDeleteAction.java | 2 +- .../rdbms/actions/RDBMSInsertAction.java | 2 +- .../rdbms/actions/RDBMSQueryAction.java | 2 +- .../rdbms/actions/RDBMSUpdateAction.java | 2 +- 8 files changed, 120 insertions(+), 45 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/JsonUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/JsonUtils.java index f713196d..b1f2b9a3 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/JsonUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/JsonUtils.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.utils; import java.io.IOException; import java.time.Instant; +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -230,63 +231,110 @@ public class JsonUtils ** Convert a json object into a QRecord ** *******************************************************************************/ - public static QRecord parseQRecord(JSONObject jsonObject, Map fields) + public static QRecord parseQRecordStrict(JSONObject jsonObject, Map fields) + { + return (parseQRecord(jsonObject, fields, true)); + } + + + + /******************************************************************************* + ** Convert a json object into a QRecord + ** + *******************************************************************************/ + public static QRecord parseQRecordLenient(JSONObject jsonObject, Map fields) + { + return (parseQRecord(jsonObject, fields, false)); + } + + + + /******************************************************************************* + ** Convert a json object into a QRecord + ** + *******************************************************************************/ + private static QRecord parseQRecord(JSONObject jsonObject, Map fields, boolean strict) { QRecord record = new QRecord(); FIELDS_LOOP: for(String fieldName : fields.keySet()) { - QFieldMetaData metaData = fields.get(fieldName); - String backendName = metaData.getBackendName() != null ? metaData.getBackendName() : fieldName; - - ///////////////////////////////////////////////////////////////////////////////////////////////// - // if the field backend name has dots in it, interpret that to mean traversal down sub-objects // - ///////////////////////////////////////////////////////////////////////////////////////////////// - JSONObject jsonObjectToUse = jsonObject; - if(backendName.contains(".")) + String originalBackendName = null; + try { - ArrayList levels = new ArrayList<>(List.of(backendName.split("\\."))); - backendName = levels.remove(levels.size() - 1); + QFieldMetaData metaData = fields.get(fieldName); + 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 levels = new ArrayList<>(List.of(backendName.split("\\."))); + backendName = levels.remove(levels.size() - 1); + + for(String level : levels) { - jsonObjectToUse = jsonObjectToUse.optJSONObject(level); - if(jsonObjectToUse == null) + try + { + jsonObjectToUse = jsonObjectToUse.optJSONObject(level); + if(jsonObjectToUse == null) + { + continue FIELDS_LOOP; + } + } + catch(Exception e) { continue FIELDS_LOOP; } } - catch(Exception e) - { - continue FIELDS_LOOP; - } } - } - 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 -> + if(jsonObjectToUse.isNull(backendName)) { - String dateTimeString = jsonObjectToUse.optString(backendName); - if(StringUtils.hasContent(dateTimeString)) - { - Instant instant = ValueUtils.getValueAsInstant(dateTimeString); - record.setValue(fieldName, instant); - } + 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); + 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)); } } diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/actions/BaseAPIActionUtil.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/actions/BaseAPIActionUtil.java index 300dd3e1..318257c7 100644 --- a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/actions/BaseAPIActionUtil.java +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/actions/BaseAPIActionUtil.java @@ -263,7 +263,7 @@ public class BaseAPIActionUtil *******************************************************************************/ protected QRecord jsonObjectToRecord(JSONObject jsonObject, Map fields) throws IOException { - QRecord record = JsonUtils.parseQRecord(jsonObject, fields); + QRecord record = JsonUtils.parseQRecordLenient(jsonObject, fields); return (record); } @@ -277,6 +277,11 @@ public class BaseAPIActionUtil int statusCode = response.getStatusLine().getStatusCode(); System.out.println(statusCode); + if(statusCode >= 400) + { + handleGetResponseError(table, response); + } + HttpEntity entity = response.getEntity(); 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); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java index 4f6d6331..94a09dbe 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java @@ -401,4 +401,14 @@ public abstract class AbstractRDBMSAction implements QActionInterface } } + + + /******************************************************************************* + ** + *******************************************************************************/ + protected String escapeIdentifier(String id) + { + return ("`" + id + "`"); + } + } diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java index 3a568f95..92b4d752 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java @@ -57,7 +57,7 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf QTableMetaData table = countInput.getTable(); 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(); List params = new ArrayList<>(); diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java index 6753fe19..36efffa4 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java @@ -186,7 +186,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte // todo sql customization - can edit sql and/or param list? String sql = "DELETE FROM " - + tableName + + escapeIdentifier(tableName) + " WHERE " + primaryKeyName + " = ?"; diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java index f4a08148..9e3b8450 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java @@ -109,7 +109,7 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte { for(List 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"); List params = new ArrayList<>(); int recordIndex = 0; diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java index ad5e42d2..c326f865 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java @@ -72,7 +72,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf .map(this::getColumnName) .collect(Collectors.joining(", ")); - String sql = "SELECT " + columns + " FROM " + tableName; + String sql = "SELECT " + columns + " FROM " + escapeIdentifier(tableName); QQueryFilter filter = queryInput.getFilter(); List params = new ArrayList<>(); diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java index 05026f16..0e03ba60 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java @@ -223,7 +223,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte .map(f -> this.getColumnName(table.getField(f)) + " = ?") .collect(Collectors.joining(", ")); - String tableName = getTableName(table); + String tableName = escapeIdentifier(getTableName(table)); return ("UPDATE " + tableName + " SET " + columns + " WHERE " + getColumnName(table.getField(table.getPrimaryKeyField())) + " ");