From ebb7e7ab45da92c06be24bc47ef3ae1192fdfd12 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 13 Jul 2023 17:10:42 -0500 Subject: [PATCH] Try to add hints about unrecognized field names (if they're in other api versions) --- .../qqq/api/actions/QRecordApiAdapter.java | 55 ++++++++++++++++++- .../api/actions/QRecordApiAdapterTest.java | 9 ++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/QRecordApiAdapter.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/QRecordApiAdapter.java index 85f0c602..beb902d5 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/QRecordApiAdapter.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/QRecordApiAdapter.java @@ -35,6 +35,8 @@ import com.kingsrook.qqq.api.model.APIVersion; import com.kingsrook.qqq.api.model.APIVersionRange; import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper; import com.kingsrook.qqq.api.model.actions.GetTableApiFieldsInput; +import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData; +import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData; import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer; import com.kingsrook.qqq.api.model.metadata.tables.ApiAssociationMetaData; @@ -289,7 +291,27 @@ public class QRecordApiAdapter if(!unrecognizedFieldNames.isEmpty()) { - throw (new QBadRequestException("Request body contained " + unrecognizedFieldNames.size() + " unrecognized field name" + StringUtils.plural(unrecognizedFieldNames) + ": " + StringUtils.joinWithCommasAndAnd(unrecognizedFieldNames))); + List otherVersionHints = new ArrayList<>(); + try + { + for(String unrecognizedFieldName : unrecognizedFieldNames) + { + String hint = lookForFieldInOtherVersions(unrecognizedFieldName, tableName, apiName, apiVersion); + if(hint != null) + { + otherVersionHints.add(hint); + } + } + } + catch(Exception e) + { + LOG.warn("Error looking for unrecognized field names in other api versions", e); + } + + throw (new QBadRequestException("Request body contained " + + (unrecognizedFieldNames.size() + " unrecognized field name" + StringUtils.plural(unrecognizedFieldNames) + ": " + StringUtils.joinWithCommasAndAnd(unrecognizedFieldNames) + ". ") + + (CollectionUtils.nullSafeIsEmpty(otherVersionHints) ? "" : StringUtils.join(" ", otherVersionHints)) + )); } return (qRecord); @@ -297,6 +319,37 @@ public class QRecordApiAdapter + /******************************************************************************* + ** + *******************************************************************************/ + private static String lookForFieldInOtherVersions(String unrecognizedFieldName, String tableName, String apiName, String apiVersion) throws QException + { + ApiInstanceMetaDataContainer apiInstanceMetaDataContainer = ApiInstanceMetaDataContainer.of(QContext.getQInstance()); + ApiInstanceMetaData apiInstanceMetaData = apiInstanceMetaDataContainer.getApiInstanceMetaData(apiName); + + List versionsWithThisField = new ArrayList<>(); + for(APIVersion supportedVersion : apiInstanceMetaData.getSupportedVersions()) + { + if(!supportedVersion.toString().equals(apiVersion)) + { + Map versionFields = getTableApiFieldMap(new ApiNameVersionAndTableName(apiName, supportedVersion.toString(), tableName)); + if(versionFields.containsKey(unrecognizedFieldName)) + { + versionsWithThisField.add(supportedVersion.toString()); + } + } + } + + if(CollectionUtils.nullSafeHasContents(versionsWithThisField)) + { + return (unrecognizedFieldName + " does not exist in version " + apiVersion + ", but does exist in versions: " + StringUtils.joinWithCommasAndAnd(versionsWithThisField) + ". "); + } + + return (null); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/actions/QRecordApiAdapterTest.java b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/actions/QRecordApiAdapterTest.java index 5494b9cb..24c6abc0 100644 --- a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/actions/QRecordApiAdapterTest.java +++ b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/actions/QRecordApiAdapterTest.java @@ -158,7 +158,8 @@ class QRecordApiAdapterTest extends BaseTest {"firstName": "Tim", "noOfShoes": 2} """), TestUtils.TABLE_NAME_PERSON, TestUtils.API_NAME, TestUtils.V2022_Q4, true)) .isInstanceOf(QBadRequestException.class) - .hasMessageContaining("unrecognized field name: noOfShoes"); + .hasMessageContaining("unrecognized field name: noOfShoes") + .hasMessageContaining("noOfShoes does not exist in version 2022.Q4, but does exist in versions: 2023.Q1"); ///////////////////////////////////////////////////////////////////////// // current version doesn't have cost field - fail if you send it to us // @@ -167,7 +168,8 @@ class QRecordApiAdapterTest extends BaseTest {"firstName": "Tim", "cost": 2} """), TestUtils.TABLE_NAME_PERSON, TestUtils.API_NAME, TestUtils.V2023_Q1, true)) .isInstanceOf(QBadRequestException.class) - .hasMessageContaining("unrecognized field name: cost"); + .hasMessageContaining("unrecognized field name: cost") + .hasMessageNotContaining("cost does not exist in version 2023.Q1, but does exist in versions: 2023.Q2"); // this field only appears in a future version, not any current/supported versions. ///////////////////////////////// // excluded field always fails // @@ -178,7 +180,8 @@ class QRecordApiAdapterTest extends BaseTest {"firstName": "Tim", "price": 2} """), TestUtils.TABLE_NAME_PERSON, TestUtils.API_NAME, version, true)) .isInstanceOf(QBadRequestException.class) - .hasMessageContaining("unrecognized field name: price"); + .hasMessageContaining("unrecognized field name: price") + .hasMessageNotContaining("price does not exist in version"); // this field never appears, so no message about when it appears. } ////////////////////////////////////////////