22
33import java .util .Date ;
44import java .util .Iterator ;
5+ import java .util .ArrayList ;
6+ import java .util .Collections ;
7+ import java .util .List ;
58import java .util .Map ;
69import java .util .Optional ;
710import java .util .concurrent .atomic .AtomicInteger ;
11+ import java .util .logging .Level ;
12+ import java .util .logging .Logger ;
813import com .fasterxml .jackson .databind .JsonNode ;
914import com .fasterxml .jackson .databind .ObjectMapper ;
1015import com .fasterxml .jackson .databind .node .JsonNodeType ;
@@ -68,6 +73,53 @@ public void setDateTimeField(boolean dateTimeField) {
6873
6974public class DynamicSearchHelper {
7075
76+ public static final String WARNINGS_EXTENSION = "teaql.dynamicSearch.warnings" ;
77+ private static final Logger LOGGER = Logger .getLogger (DynamicSearchHelper .class .getName ());
78+
79+ public static List <DynamicSearchWarning > warningsOf (BaseRequest request ) {
80+ Object warnings = request .getExtensions ().get (WARNINGS_EXTENSION );
81+ if (!(warnings instanceof List <?>)) {
82+ return Collections .emptyList ();
83+ }
84+ List <DynamicSearchWarning > result = new ArrayList <>();
85+ for (Object warning : (List <?>) warnings ) {
86+ if (warning instanceof DynamicSearchWarning ) {
87+ result .add ((DynamicSearchWarning ) warning );
88+ }
89+ }
90+ return Collections .unmodifiableList (result );
91+ }
92+
93+ protected void warnUnknownField (BaseRequest request , String clause , String fieldPath ) {
94+ DynamicSearchWarning warning =
95+ new DynamicSearchWarning (
96+ DynamicSearchWarning .UNKNOWN_FIELD ,
97+ request .getTypeName (),
98+ clause ,
99+ fieldPath );
100+ Object existing = request .getExtensions ().get (WARNINGS_EXTENSION );
101+ List <DynamicSearchWarning > warnings ;
102+ if (existing instanceof List <?>) {
103+ @ SuppressWarnings ("unchecked" )
104+ List <DynamicSearchWarning > typed = (List <DynamicSearchWarning >) existing ;
105+ warnings = typed ;
106+ }
107+ else {
108+ warnings = new ArrayList <>();
109+ request .putExtension (WARNINGS_EXTENSION , warnings );
110+ }
111+ warnings .add (warning );
112+ LOGGER .log (
113+ Level .WARNING ,
114+ "Ignored unknown dynamic search field: code={0}, requestType={1}, clause={2}, fieldPath={3}" ,
115+ new Object [] {
116+ warning .getCode (),
117+ warning .getRequestType (),
118+ warning .getClause (),
119+ warning .getFieldPath ()
120+ });
121+ }
122+
71123 protected static JsonNode jsonFromString (String jsonExpr ) {
72124 try {
73125 ObjectMapper objectMapper = new ObjectMapper ();
@@ -129,6 +181,9 @@ public void addJsonFilter(BaseRequest baseRequest, JsonNode jsonNode) {
129181 String fieldName = field .getKey ();
130182
131183 if (!baseRequest .isOneOfSelfField (fieldName )) {
184+ if (!fieldName .startsWith ("_" )) {
185+ warnUnknownField (baseRequest , "FILTER" , fieldName );
186+ }
132187 continue ;
133188 }
134189 JsonNode fieldValue = field .getValue ();
@@ -155,12 +210,31 @@ protected boolean handleChainField(
155210 if (fieldNames .length < 2 ) {
156211 return true ; // need to continue
157212 }
213+ SearchCriteria originalCriteria = rootRequest .getSearchCriteria ();
158214 BaseRequest currentRequest = rootRequest ;
159215 for (int i = 0 ; i < fieldNames .length - 1 ; i ++) {
160- Optional <BaseRequest > optional = currentRequest .subRequestOfFieldName (fieldNames [i ]);
216+ Optional <BaseRequest > optional ;
217+ try {
218+ optional = currentRequest .subRequestOfFieldName (fieldNames [i ]);
219+ }
220+ catch (IllegalArgumentException exception ) {
221+ rootRequest .replaceSearchCriteria (originalCriteria );
222+ warnUnknownField (rootRequest , "FILTER" , fieldName );
223+ return false ;
224+ }
225+ if (optional .isEmpty ()) {
226+ rootRequest .replaceSearchCriteria (originalCriteria );
227+ warnUnknownField (rootRequest , "FILTER" , fieldName );
228+ return false ;
229+ }
161230 currentRequest = optional .get ();
162231 }
163232 final String lastSegmentOfField = fieldNames [fieldNames .length - 1 ];
233+ if (!currentRequest .isOneOfSelfField (lastSegmentOfField )) {
234+ rootRequest .replaceSearchCriteria (originalCriteria );
235+ warnUnknownField (rootRequest , "FILTER" , fieldName );
236+ return false ;
237+ }
164238 // last segment of field, use it as value
165239 currentRequest .appendSearchCriteria (
166240 currentRequest .createBasicSearchCriteria (
@@ -356,6 +430,7 @@ public void addJsonOrderBy(BaseRequest baseRequest, JsonNode jsonNode) {
356430 // single text
357431 if (fieldValue .isTextual ()) {
358432 if (!baseRequest .isOneOfSelfField (fieldValue .asText ())) {
433+ warnUnknownField (baseRequest , "ORDER_BY" , fieldValue .asText ());
359434 return ;
360435 }
361436 this .addOrderBy (baseRequest , fieldValue .asText (), false );
@@ -381,6 +456,7 @@ public void addJsonOrderBy(BaseRequest baseRequest, JsonNode jsonNode) {
381456 protected void addSingleJsonOrderBy (BaseRequest baseRequest , JsonNode jsonValueNode ) {
382457 String field = jsonValueNode .get ("field" ).asText ();
383458 if (!baseRequest .isOneOfSelfField (field )) {
459+ warnUnknownField (baseRequest , "ORDER_BY" , field );
384460 return ;
385461 }
386462 Boolean useAsc = jsonValueNode .get ("useAsc" ).booleanValue ();
0 commit comments