diff --git a/README.md b/README.md index 91d9a31..8bd0622 100644 --- a/README.md +++ b/README.md @@ -223,16 +223,37 @@ api.pagination.max-page-size=500 ## Error Handling -The library throws a `QueryException` when security or syntax rules are violated: +The library provides a hierarchy of exceptions to distinguish between client-side validation errors and developer-side configuration issues. All exceptions extend the base `QueryException`. -- Filtering on a non-`@RsqlFilterable` field. -- Using a restricted operator on a field. -- Sorting on a non-`@Sortable` field. -- Invalid RSQL syntax. +### Exception Hierarchy + +- **`QueryValidationException`**: Thrown when an API consumer provides invalid input. These should typically be returned as a `400 Bad Request`. + - Filtering on a non-`@RsqlFilterable` field. + - Using a disallowed operator on a field. + - Sorting on a non-`@Sortable` field. + - Using an original field name when a mapping alias is required (`allowOriginalFieldName = false`). + - Malformed RSQL syntax. +- **`QueryConfigurationException`**: Thrown when the library or entity mapping is misconfigured by the developer. These should typically be treated as a `500 Internal Server Error`. + - Custom operators referenced in `@RsqlFilterable` that are not registered. + - Field mappings pointing to non-existent fields on the entity. + +### Handling Exceptions ```java @ControllerAdvice public class GlobalExceptionHandler { + + @ExceptionHandler(QueryValidationException.class) + public ResponseEntity handleValidationException(QueryValidationException ex) { + return ResponseEntity.badRequest().body(ex.getMessage()); + } + + @ExceptionHandler(QueryConfigurationException.class) + public ResponseEntity handleConfigurationException(QueryConfigurationException ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal configuration error"); + } + + // Alternatively, catch the base exception for unified handling @ExceptionHandler(QueryException.class) public ResponseEntity handleQueryException(QueryException ex) { return ResponseEntity.badRequest().body(ex.getMessage()); diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolver.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolver.java index dc9aa70..53f7463 100644 --- a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolver.java +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolver.java @@ -2,7 +2,7 @@ import in.co.akshitbansal.springwebquery.annotation.RestrictedPageable; import in.co.akshitbansal.springwebquery.annotation.Sortable; -import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import in.co.akshitbansal.springwebquery.util.ReflectionUtil; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -36,7 +36,7 @@ * on fields explicitly annotated with {@link Sortable}. *

* If a requested sort field is not annotated as {@link Sortable}, a - * {@link QueryException} is thrown. + * {@link QueryValidationException} is thrown. */ @RequiredArgsConstructor public class RestrictedPageableArgumentResolver implements HandlerMethodArgumentResolver { @@ -73,7 +73,7 @@ public boolean supportsParameter(MethodParameter parameter) { *

  • Delegate parsing of page, size, and sort parameters to {@link #delegate}.
  • *
  • Retrieve the target entity class from the {@link RestrictedPageable} annotation.
  • *
  • Validate each requested {@link Sort.Order} against the entity's sortable fields. - * If a field is not annotated with {@link Sortable}, a {@link QueryException} is thrown.
  • + * If a field is not annotated with {@link Sortable}, a {@link QueryValidationException} is thrown. * * * @param methodParameter the method parameter for which the value should be resolved @@ -81,7 +81,7 @@ public boolean supportsParameter(MethodParameter parameter) { * @param webRequest the current request * @param binderFactory a factory for creating WebDataBinder instances (can be {@code null}) * @return a {@link Pageable} object containing page, size, and validated sort information - * @throws QueryException if any requested sort field is not marked as {@link Sortable} + * @throws QueryValidationException if any requested sort field is not marked as {@link Sortable} */ @Override public Pageable resolveArgument( @@ -101,10 +101,18 @@ public Pageable resolveArgument( for(Sort.Order order : pageable.getSort()) { String fieldName = order.getProperty(); // Resolve the field on the entity (including inherited fields) - Field field = ReflectionUtil.resolveField(entityClass, fieldName); + Field field; + try { + field = ReflectionUtil.resolveField(entityClass, fieldName); + } + catch (Exception ex) { + throw new QueryValidationException(MessageFormat.format( + "Unknown field ''{0}''", fieldName + ), ex); + } // Reject sorting on fields not explicitly marked as sortable if(!field.isAnnotationPresent(Sortable.class)) - throw new QueryException(MessageFormat.format("Sorting is not allowed on the field ''{0}''", fieldName)); + throw new QueryValidationException(MessageFormat.format("Sorting is not allowed on the field ''{0}''", fieldName)); } return pageable; diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolver.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolver.java index e7b6c05..70f293e 100644 --- a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolver.java +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolver.java @@ -8,6 +8,7 @@ import in.co.akshitbansal.springwebquery.annotation.RsqlFilterable; import in.co.akshitbansal.springwebquery.annotation.RsqlSpec; import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import in.co.akshitbansal.springwebquery.operator.RsqlCustomOperator; import in.co.akshitbansal.springwebquery.operator.RsqlOperator; import io.github.perplexhub.rsql.QuerySupport; @@ -144,7 +145,7 @@ public boolean supportsParameter(MethodParameter parameter) { * @param binderFactory the data binder factory * @return a {@link Specification} representing the RSQL query, * or an unrestricted Specification if the query is absent - * @throws QueryException if the RSQL query is invalid or violates + * @throws QueryValidationException if the RSQL query is invalid or violates * {@link RsqlFilterable} constraints */ @Override @@ -192,10 +193,7 @@ public Specification resolveArgument( return RSQLJPASupport.toSpecification(querySupport); } catch (RSQLParserException ex) { - throw new QueryException("Unable to parse rsql query param", ex); - } - catch (QueryException ex) { - throw ex; + throw new QueryValidationException("Unable to parse rsql query param", ex); } } } diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitor.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitor.java index a9d2b41..1d3739d 100644 --- a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitor.java +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitor.java @@ -3,7 +3,9 @@ import cz.jirutka.rsql.parser.ast.*; import in.co.akshitbansal.springwebquery.annotation.FieldMapping; import in.co.akshitbansal.springwebquery.annotation.RsqlFilterable; +import in.co.akshitbansal.springwebquery.exception.QueryConfigurationException; import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import in.co.akshitbansal.springwebquery.operator.RsqlCustomOperator; import in.co.akshitbansal.springwebquery.operator.RsqlOperator; import in.co.akshitbansal.springwebquery.util.ReflectionUtil; @@ -123,8 +125,9 @@ public Void visit(OrNode orNode, Void unused) { * @param comparisonNode the comparison node * @param unused unused parameter * @return null - * @throws QueryException if the field does not exist, is not filterable, + * @throws QueryValidationException if the field does not exist, is not filterable, * or the operator is not allowed + * @throws QueryConfigurationException if a custom operator or field mapping is misconfigured */ @Override public Void visit(ComparisonNode comparisonNode, Void unused) { @@ -136,7 +139,8 @@ public Void visit(ComparisonNode comparisonNode, Void unused) { * Validates a comparison node against the entity class. * * @param node the comparison node to validate - * @throws QueryException if the field is not allowed or operator is invalid + * @throws QueryValidationException if the field is not allowed or operator is invalid + * @throws QueryConfigurationException if the field mapping is misconfigured */ private void validate(ComparisonNode node) { // Extract the field name and operator from the RSQL node @@ -146,7 +150,7 @@ private void validate(ComparisonNode node) { // Find if there exists a field mapping with original field name and throw error if use is not allowed FieldMapping originalFieldMapping = originalFieldMappings.get(fieldName); if(originalFieldMapping != null && !originalFieldMapping.allowOriginalFieldName()) - throw new QueryException(MessageFormat.format( + throw new QueryValidationException(MessageFormat.format( "Unknown field ''{0}''", fieldName )); @@ -156,13 +160,21 @@ private void validate(ComparisonNode node) { if(fieldMapping != null) fieldName = fieldMapping.field(); // Resolve the Field object from the entity class using reflection - Field field = ReflectionUtil.resolveField(entityClass, fieldName); + Field field; + try { + field = ReflectionUtil.resolveField(entityClass, fieldName); + } + catch (Exception ex) { + throw new QueryValidationException(MessageFormat.format( + "Unknown field ''{0}''", fieldName + ), ex); + } // Retrieve the RsqlFilterable annotation on the field (if present) RsqlFilterable filterable = field.getAnnotation(RsqlFilterable.class); // Throw exception if the field is not annotated as filterable - if(filterable == null) throw new QueryException(MessageFormat.format( + if(filterable == null) throw new QueryValidationException(MessageFormat.format( "Filtering not allowed on field ''{0}''", reqFieldName )); @@ -185,7 +197,7 @@ private void validate(ComparisonNode node) { .collect(Collectors.toSet()); // Throw exception if the provided operator is not in the allowed set - if(!allowedOperators.contains(operator)) throw new QueryException(MessageFormat.format( + if(!allowedOperators.contains(operator)) throw new QueryValidationException(MessageFormat.format( "Operator ''{0}'' not allowed on field ''{1}''", operator, reqFieldName )); } @@ -195,11 +207,11 @@ private void validate(ComparisonNode node) { * * @param clazz the custom operator class to look up * @return the registered custom operator instance - * @throws QueryException if the custom operator class is not registered + * @throws QueryConfigurationException if the custom operator class is not registered */ private RsqlCustomOperator getCustomOperator(Class clazz) { RsqlCustomOperator operator = customOperators.get(clazz); - if(operator == null) throw new QueryException(MessageFormat.format( + if(operator == null) throw new QueryConfigurationException(MessageFormat.format( "Custom operator ''{0}'' referenced in @RsqlFilterable is not registered", clazz.getSimpleName() )); return operator; diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryConfigurationException.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryConfigurationException.java new file mode 100644 index 0000000..c56386c --- /dev/null +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryConfigurationException.java @@ -0,0 +1,33 @@ +package in.co.akshitbansal.springwebquery.exception; + +/** + * Exception thrown when the library is misconfigured by the developer. + *

    + * This exception indicates an internal configuration error, such as referencing + * a custom operator that has not been registered with the {@code RsqlSpecificationArgumentResolver}. + *

    + * + *

    This exception is intended to be treated as a 5xx server error + * as it highlights a development-time configuration issue.

    + */ +public class QueryConfigurationException extends QueryException { + + /** + * Constructs a new query configuration exception with the specified detail message. + * + * @param message the detail message explaining the reason for the configuration error + */ + public QueryConfigurationException(String message) { + super(message); + } + + /** + * Constructs a new query configuration exception with the specified detail message and cause. + * + * @param message the detail message explaining the reason for the configuration error + * @param cause the underlying cause of the configuration error + */ + public QueryConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryException.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryException.java index 5eccb9f..9a72e02 100644 --- a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryException.java +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryException.java @@ -1,30 +1,28 @@ package in.co.akshitbansal.springwebquery.exception; -import in.co.akshitbansal.springwebquery.annotation.FieldMapping; import in.co.akshitbansal.springwebquery.annotation.RsqlFilterable; import in.co.akshitbansal.springwebquery.annotation.Sortable; /** - * Exception thrown when an RSQL query or pagination request violates - * configured field or operator restrictions. + * Base exception thrown for all RSQL query or pagination related errors. *

    - * This exception is typically thrown in the following scenarios: + * This class serves as the parent for more specific exceptions that distinguish + * between client-side validation errors and developer-side configuration errors: + *

    *
      - *
    • A query attempts to filter on a field not annotated with - * {@link RsqlFilterable}
    • - *
    • A query uses a default or custom operator not allowed for a specific field
    • - *
    • A query uses original field name when the behavior is disabled via {@link FieldMapping#allowOriginalFieldName()}
    • - *
    • A sort request targets a field not annotated with - * {@link Sortable}
    • - *
    • A field referenced in a query does not exist on the entity
    • - *
    • A custom operator referenced in {@link RsqlFilterable} is not registered
    • - *
    • The RSQL query syntax is malformed or cannot be parsed
    • + *
    • {@link QueryValidationException}: Thrown when an API consumer provides + * an invalid query or violates validation rules (e.g., malformed RSQL, + * disallowed operators, non-filterable fields).
    • + *
    • {@link QueryConfigurationException}: Thrown when the library or + * entity mapping is misconfigured by the developer (e.g., unregistered + * custom operators).
    • *
    * - *

    This is a runtime exception and is intended to be caught and handled - * at the controller or advice layer to provide meaningful error responses - * to API clients.

    + *

    Using this base exception in a controller advice or catch block allows + * handling all query-related errors in a unified manner.

    * + * @see QueryValidationException + * @see QueryConfigurationException * @see RsqlFilterable * @see Sortable */ diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryValidationException.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryValidationException.java new file mode 100644 index 0000000..cb9116d --- /dev/null +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/exception/QueryValidationException.java @@ -0,0 +1,35 @@ +package in.co.akshitbansal.springwebquery.exception; + +/** + * Exception thrown when an API consumer provides an invalid RSQL query, + * sorting request, or filter parameters. + *

    + * This exception indicates that the request itself is malformed or violates + * configured validation rules (e.g., filtering on a non-filterable field, + * using a disallowed operator, or providing invalid RSQL syntax). + *

    + * + *

    This exception is intended to be caught and returned as a 4xx client + * error to the consumer.

    + */ +public class QueryValidationException extends QueryException { + + /** + * Constructs a new query validation exception with the specified detail message. + * + * @param message the detail message explaining the reason for the validation failure + */ + public QueryValidationException(String message) { + super(message); + } + + /** + * Constructs a new query validation exception with the specified detail message and cause. + * + * @param message the detail message explaining the reason for the validation failure + * @param cause the underlying cause of the validation failure (e.g., {@link cz.jirutka.rsql.parser.RSQLParserException}) + */ + public QueryValidationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/util/ReflectionUtil.java b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/util/ReflectionUtil.java index c2ebe4d..5426844 100644 --- a/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/util/ReflectionUtil.java +++ b/spring-web-query-core/src/main/java/in/co/akshitbansal/springwebquery/util/ReflectionUtil.java @@ -87,8 +87,8 @@ private static Field resolveFieldUpHierarchy(Class type, String name) { current = current.getSuperclass(); } } - throw new QueryException(MessageFormat.format( - "Unknown field ''{0}''", name + throw new RuntimeException(MessageFormat.format( + "Field ''{0}'' not found in class hierarchy of {1}", name, type )); } diff --git a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolverTest.java b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolverTest.java index c1923db..eee85f0 100644 --- a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolverTest.java +++ b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RestrictedPageableArgumentResolverTest.java @@ -2,7 +2,7 @@ import in.co.akshitbansal.springwebquery.annotation.RestrictedPageable; import in.co.akshitbansal.springwebquery.annotation.Sortable; -import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import org.junit.jupiter.api.Test; import org.springframework.core.MethodParameter; import org.springframework.data.domain.Pageable; @@ -13,10 +13,7 @@ import java.lang.reflect.Method; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class RestrictedPageableArgumentResolverTest { @@ -55,7 +52,7 @@ void resolveArgument_rejectsNonSortableField() throws NoSuchMethodException { MethodParameter parameter = new MethodParameter(method, 0); NativeWebRequest request = requestWithSort("secret,desc"); - assertThrows(QueryException.class, () -> resolver.resolveArgument(parameter, null, request, null)); + assertThrows(QueryValidationException.class, () -> resolver.resolveArgument(parameter, null, request, null)); } private static NativeWebRequest requestWithSort(String sort) { diff --git a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolverTest.java b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolverTest.java index ec21fd9..974722d 100644 --- a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolverTest.java +++ b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/RsqlSpecificationArgumentResolverTest.java @@ -4,7 +4,7 @@ import in.co.akshitbansal.springwebquery.annotation.FieldMapping; import in.co.akshitbansal.springwebquery.annotation.RsqlFilterable; import in.co.akshitbansal.springwebquery.annotation.RsqlSpec; -import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import in.co.akshitbansal.springwebquery.operator.RsqlCustomOperator; import in.co.akshitbansal.springwebquery.operator.RsqlOperator; import io.github.perplexhub.rsql.RSQLCustomPredicateInput; @@ -59,7 +59,7 @@ void resolveArgument_rejectsInvalidRsqlSyntax() throws NoSuchMethodException { MethodParameter parameter = new MethodParameter(method, 0); NativeWebRequest webRequest = requestWithFilter("name=="); - assertThrows(QueryException.class, () -> resolver.resolveArgument(parameter, null, webRequest, null)); + assertThrows(QueryValidationException.class, () -> resolver.resolveArgument(parameter, null, webRequest, null)); } @Test @@ -77,7 +77,7 @@ void resolveArgument_rejectsOriginalMappedFieldWhenNotAllowed() throws NoSuchMet MethodParameter parameter = new MethodParameter(method, 0); NativeWebRequest webRequest = requestWithFilter("name==john"); - assertThrows(QueryException.class, () -> resolver.resolveArgument(parameter, null, webRequest, null)); + assertThrows(QueryValidationException.class, () -> resolver.resolveArgument(parameter, null, webRequest, null)); } @Test diff --git a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitorTest.java b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitorTest.java index c7f67fe..7e5c27a 100644 --- a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitorTest.java +++ b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/ValidationRSQLVisitorTest.java @@ -4,7 +4,8 @@ import cz.jirutka.rsql.parser.ast.ComparisonOperator; import in.co.akshitbansal.springwebquery.annotation.FieldMapping; import in.co.akshitbansal.springwebquery.annotation.RsqlFilterable; -import in.co.akshitbansal.springwebquery.exception.QueryException; +import in.co.akshitbansal.springwebquery.exception.QueryConfigurationException; +import in.co.akshitbansal.springwebquery.exception.QueryValidationException; import in.co.akshitbansal.springwebquery.operator.RsqlCustomOperator; import in.co.akshitbansal.springwebquery.operator.RsqlOperator; import io.github.perplexhub.rsql.RSQLCustomPredicateInput; @@ -42,8 +43,8 @@ void visit_allowsFilterableFieldWithAllowedOperator() { @Test void visit_rejectsUnknownField() { ValidationRSQLVisitor visitor = new ValidationRSQLVisitor(TestEntity.class, new FieldMapping[]{}, Collections.emptySet()); - QueryException ex = assertThrows( - QueryException.class, + QueryValidationException ex = assertThrows( + QueryValidationException.class, () -> parser.parse("missing==x").accept(visitor) ); assertEquals("Unknown field 'missing'", ex.getMessage()); @@ -52,8 +53,8 @@ void visit_rejectsUnknownField() { @Test void visit_rejectsNonFilterableField() { ValidationRSQLVisitor visitor = new ValidationRSQLVisitor(TestEntity.class, new FieldMapping[]{}, Collections.emptySet()); - QueryException ex = assertThrows( - QueryException.class, + QueryValidationException ex = assertThrows( + QueryValidationException.class, () -> parser.parse("age==10").accept(visitor) ); assertEquals("Filtering not allowed on field 'age'", ex.getMessage()); @@ -62,8 +63,8 @@ void visit_rejectsNonFilterableField() { @Test void visit_rejectsDisallowedOperator() { ValidationRSQLVisitor visitor = new ValidationRSQLVisitor(TestEntity.class, new FieldMapping[]{}, Collections.emptySet()); - QueryException ex = assertThrows( - QueryException.class, + QueryValidationException ex = assertThrows( + QueryValidationException.class, () -> parser.parse("name!=john").accept(visitor) ); assertEquals("Operator '!=' not allowed on field 'name'", ex.getMessage()); @@ -81,8 +82,8 @@ void visit_rejectsUnregisteredCustomOperator() { Set> customOperators = Set.of(new MockCustomOperator()); ValidationRSQLVisitor visitor = new ValidationRSQLVisitor(TestEntityWithCustom.class, new FieldMapping[]{}, Collections.emptySet()); RSQLParser customParser = configuredParser(customOperators); - QueryException ex = assertThrows( - QueryException.class, + QueryConfigurationException ex = assertThrows( + QueryConfigurationException.class, () -> customParser.parse("name=mock=value").accept(visitor) ); assertEquals("Custom operator 'MockCustomOperator' referenced in @RsqlFilterable is not registered", ex.getMessage()); diff --git a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/util/ReflectionUtilTest.java b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/util/ReflectionUtilTest.java index d3ec21b..f2399bc 100644 --- a/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/util/ReflectionUtilTest.java +++ b/spring-web-query-core/src/test/java/in/co/akshitbansal/springwebquery/util/ReflectionUtilTest.java @@ -1,6 +1,5 @@ package in.co.akshitbansal.springwebquery.util; -import in.co.akshitbansal.springwebquery.exception.QueryException; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; @@ -34,11 +33,11 @@ void resolveField_resolvesNestedFieldThroughArray() { @Test void resolveField_throwsForUnknownSegment() { - QueryException ex = assertThrows( - QueryException.class, + RuntimeException ex = assertThrows( + RuntimeException.class, () -> ReflectionUtil.resolveField(ParentEntity.class, "children.unknown") ); - assertEquals("Unknown field 'unknown'", ex.getMessage()); + assertEquals("Field 'unknown' not found in class hierarchy of class in.co.akshitbansal.springwebquery.util.ReflectionUtilTest$NestedEntity", ex.getMessage()); } private static class BaseEntity {