This is an automated email from the ASF dual-hosted git repository.
diveshdut pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new 31fc974cd1 OFBIZ-13457 Add REST sort order-by alias resolver (#1556)
31fc974cd1 is described below
commit 31fc974cd12372af3ccd9773563930176ac97fab
Author: Divesh Dutta <[email protected]>
AuthorDate: Tue Aug 4 15:16:52 2026 +0530
OFBIZ-13457 Add REST sort order-by alias resolver (#1556)
## Summary
Adds a generic REST framework helper for resolving public `sort`
expressions into EntityQuery-compatible order-by fields.
## Changes
- Adds `RestApiUtil.resolveOrderBy(...)`
- Reuses existing REST sort syntax validation
- Supports endpoint-defined public sort aliases mapped to entity fields
- Preserves descending sort syntax such as `-displayName`
- Appends default order-by fields as stable tie-breakers
- Rejects unsupported, malformed, or duplicate mapped fields
- Adds focused unit coverage in `RestApiUtilPaginationTest`
---
.../org/apache/ofbiz/ws/rs/util/RestApiUtil.java | 49 ++++++++++++++++++++++
.../ws/rs/util/RestApiUtilPaginationTest.java | 27 ++++++++++++
2 files changed, 76 insertions(+)
diff --git
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
index b8d3a9b7f3..22e14ffab7 100644
---
a/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
+++
b/framework/rest-api/src/main/java/org/apache/ofbiz/ws/rs/util/RestApiUtil.java
@@ -362,6 +362,55 @@ public final class RestApiUtil {
return validatedFields;
}
+ /**
+ * Resolves a REST sort expression into EntityQuery order-by fields.
+ * Endpoint contracts can expose semantic sort names while entities use
+ * different field names, so public names are validated before mapping.
+ *
+ * @param sortExpression the requested sort expression
+ * @param sortFieldMap endpoint-supported sort fields mapped to entity
fields
+ * @param defaultOrderBy fallback order fields and stable tie-breakers
+ * @return entity order-by fields suitable for {@code EntityQuery.orderBy}
+ * @throws IllegalArgumentException when a token is malformed, unsupported,
+ * or maps to a duplicate entity field
+ */
+ public static List<String> resolveOrderBy(String sortExpression,
Map<String, String> sortFieldMap, List<String> defaultOrderBy) {
+ List<String> orderBy = new ArrayList<>();
+ if (UtilValidate.isEmpty(sortExpression)) {
+ if (UtilValidate.isNotEmpty(defaultOrderBy)) {
+ orderBy.addAll(defaultOrderBy);
+ }
+ return orderBy;
+ }
+
+ Set<String> allowedFields = UtilValidate.isNotEmpty(sortFieldMap) ?
sortFieldMap.keySet() : null;
+ List<String> validatedFields = validateSortFields(sortExpression,
allowedFields);
+ Set<String> seenEntityFields = new HashSet<>();
+ for (String requestedField : validatedFields) {
+ boolean descending = requestedField.startsWith("-");
+ String sortKey = descending ? requestedField.substring(1) :
requestedField;
+ String entityField = UtilValidate.isNotEmpty(sortFieldMap) ?
sortFieldMap.get(sortKey) : sortKey;
+ if (UtilValidate.isEmpty(entityField)) {
+ throw new IllegalArgumentException("Unsupported sort field: "
+ sortKey);
+ }
+ if (!seenEntityFields.add(entityField)) {
+ throw new IllegalArgumentException("Duplicate sort field: " +
entityField);
+ }
+ orderBy.add((descending ? "-" : "") + entityField);
+ }
+
+ if (UtilValidate.isNotEmpty(defaultOrderBy)) {
+ for (String defaultField : defaultOrderBy) {
+ String normalizedDefaultField = defaultField.startsWith("-") ?
defaultField.substring(1) : defaultField;
+ if (!seenEntityFields.contains(normalizedDefaultField)) {
+ orderBy.add(defaultField);
+ seenEntityFields.add(normalizedDefaultField);
+ }
+ }
+ }
+ return orderBy;
+ }
+
/**
* Validates candidate filter parameters against an optional
endpoint-defined
* allowlist while preserving insertion order.
diff --git
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/util/RestApiUtilPaginationTest.java
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/util/RestApiUtilPaginationTest.java
index cf2c676270..262de1c468 100644
---
a/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/util/RestApiUtilPaginationTest.java
+++
b/framework/rest-api/src/test/java/org/apache/ofbiz/ws/rs/util/RestApiUtilPaginationTest.java
@@ -79,6 +79,33 @@ public final class RestApiUtilPaginationTest {
assertEquals("Duplicate sort field: fieldOne", exception.getMessage());
}
+ @Test
+ public void resolvesOrderByWithFieldAliasesAndStableDefaults() {
+ List<String> orderBy = RestApiUtil.resolveOrderBy("-displayName",
+ UtilMisc.toMap("displayName", "entityName", "externalId",
"entityId"),
+ List.of("entityName", "entityId"));
+
+ assertEquals(List.of("-entityName", "entityId"), orderBy);
+ }
+
+ @Test
+ public void resolvesOrderByToDefaultsWhenSortMissing() {
+ List<String> defaultOrderBy = List.of("entityName", "entityId");
+
+ assertEquals(defaultOrderBy, RestApiUtil.resolveOrderBy(null,
+ UtilMisc.toMap("externalId", "entityId"), defaultOrderBy));
+ }
+
+ @Test
+ public void rejectsOrderByAliasesThatMapToDuplicateEntityFields() {
+ IllegalArgumentException exception =
assertThrows(IllegalArgumentException.class, () ->
+ RestApiUtil.resolveOrderBy("displayName,-entityName",
+ UtilMisc.toMap("displayName", "entityName",
"entityName", "entityName"),
+ List.of("entityId")));
+
+ assertEquals("Duplicate sort field: entityName",
exception.getMessage());
+ }
+
@Test
public void serializesAvailableRelationsAsHttpLinkHeader() {
Map<String, Object> links = new LinkedHashMap<>();