-
Notifications
You must be signed in to change notification settings - Fork 357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: object list special filter queries [DHIS2-17200] #19046
Conversation
...c/main/java/org/hisp/dhis/webapi/controller/organisationunit/OrganisationUnitController.java
Fixed
Show fixed
Hide fixed
@@ -250,6 +251,14 @@ static UserDetails createUserDetails( | |||
|
|||
void setId(Long id); | |||
|
|||
default boolean canIssueUserRole(UserRole role, boolean canGrantOwnUserRole) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: An ID based version of the existing canIssueUserRole
in User
case "null" -> Restrictions.isNull(path); | ||
case "!null" -> Restrictions.isNotNull(path); | ||
case "empty" -> Restrictions.isEmpty(path); | ||
default -> throw new QueryParserException("`" + operator + "` is not a valid operator."); | ||
}; | ||
} | ||
|
||
@Nonnull | ||
@SuppressWarnings("rawtypes") | ||
private Collection parseValues(Property property, Class<?> valueType, Object arg) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: duplication extracted to this method
return values; | ||
} | ||
|
||
private Collection<Disjunction> getDisjunctionsFromCustomMentions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: logic extracted from outside of the engine to be inside as it operates on the 3 parts on a filter
@@ -108,68 +111,11 @@ public List<T> query(Query query) { | |||
CriteriaQuery<T> criteriaQuery = builder.createQuery(klass); | |||
Root<T> root = criteriaQuery.from(klass); | |||
|
|||
if (query.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: This path was eliminated as it should be equal to the "else" path except for the starting point being an empty conjunction.
Note that this path did not add orders because isEmpty
is the absence of filters and orders. In the refactored code the effect is the same but the orders are always added when non-empty.
o -> | ||
o.isAscending() | ||
? builder.asc(root.get(o.getProperty().getName())) | ||
: builder.desc(root.get(o.getProperty().getName()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: I think using getName()
instead of getFieldName()
was a bug - new code uses a common extracted method for fetch and count query
boolean hasUserGroupFilter = filters.stream().anyMatch(f -> f.startsWith("userGroups.")); | ||
params.setPrefetchUserGroups(hasUserGroupFilter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: This is no longer needed because the query will only result in a list of UIDs
… expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* fix: non-standard object list queries [DHIS2-17200] * fix: users may use standard pager * refactor: special filters as pre-stage query for ids * chore: revert now unnecessary change * refactor: use query filters for OU special params * fix: failing integration test causes * refactor: parameter objects for getObject and getObjectList * fix: dependency issues * fix: params in tests * fix: don't order count query, use getFieldName() for properties in criteria API * fix: fields parameter and filter and order parameter mapping * fix: strip regex from path variables for path * fix: filter parameter splitting * refactor: always use AbstractCrudController with list params parameter * refactor: always use object list parameter for read-only CRUD controller * fix: broken superclass and import * fix: add missing @EntityType definitions when/where used * fix: metadata import service * fix: filters on getObject * fix: dimension controller issues * fix: maven dependencies * fix: attempt to disable dependency analzye run for e2e tests * fix: sonar issues and add tests * fix: always add sorting and cache hints in JPA query engine * fix: more sonar issues * refactor: user special filters as UID pre-query, checked exceptions in filter creation * fix: query user IDs * fix: user query for IDs and always false detection * fix: sonar TODOs and issues * fix: in operator with empty value list is always false * chore: cleanup OU special filters * fix: code scanning alert no. 9132: User-controlled data in arithmetic expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Quality Gate passedIssues Measures |
Thir PR was merged but somehow it does not show as merged, therefore I am closing it now |
Summary
Object list (
GET
for/api/dataElement
,/api/users
, ...) sometimes support special filters that are not specified viafilter=
but dedicated request parameters likeinactiveSince
for users. When such filters are involved the paging and total count could be off. The cause was that special filters run a separate query, the result of that was in-memory filtered in the standard query engine. While that works for the page results the totals and the paging cannot be made correct as neither the first nor the second step see the full picture.This PR fixes that by integrating special filters in one of two ways:
This effectively means the main query running in the query engine will always see the full picture and thus have correct totals and paging.
In a few cases the starting list a query runs against is not a DB table but computed by calling another service method custom to the use case. In such cases there is always a pure in-memory filtering performed by the query service.
For totals to be correct these will call
query
2 times, once without paging for the total count, once with paging for the result page. This isn't a change but has been cleaned up to follow the same pattern (use mostly the same code).Performance
In many instances this also improves performance and resource consumption as only a single query runs which does paging in the DB instead of loading all matches into memory and page based on that list.
Programmatic Query Modifiers
There are two ways to modify the query programmatically:
addFilter
oraddOrder
Criterion
to theQuery
before it runsThe second is required since the first cannot express more complex filters which were needed to implement the existing special filters.
Object List Parameter Object
Special filters are provided by a method that can be overridden by controllers. In order to give them access to the special filters they define the parameter object needed to become a generic of the abstract controller. Only this way a endpoint method with the same method signature can have varying parameters. This required a refactoring that would introduce such a parameter as the only alternative would have been to keep relying on
Map<String,String>
for parameters which makes them entirely in-transparent.Automatic Testing
Some tests got adjusted, some new one added.
Manual Testing
A good way to test might be to use the OpenAPI documentation to learn about the special filters that do exist and then use them and compare the results with older versions.
Endpoints with additional filters
/api/organisationUnits
/api/users
/api/userRoles
/api/messageConversations
/api/dataElementOperands
/api/programs
/api/trackedEntityAttributes
/api/validationRules