From d2b80a51110e12c8a3fb9f34e540b64e8a68e531 Mon Sep 17 00:00:00 2001 From: Jan Bernitt Date: Mon, 25 Nov 2024 16:38:00 +0100 Subject: [PATCH] refactor: object list special filter queries [DHIS2-17200] (#19046) * 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> --- .github/workflows/run-api-tests.yml | 4 +- .../common/IdentifiableObjectManager.java | 3 - .../java/org/hisp/dhis/common/OpenApi.java | 1 + .../common/collection/CollectionUtils.java | 1 + .../org/hisp/dhis/fieldfilter/Defaults.java | 0 .../hisp/dhis/fieldfiltering/FieldPreset.java | 2 +- .../dhis/interpretation/MentionUtils.java | 14 - .../dhis/message/MessageConversation.java | 11 +- .../OrganisationUnitService.java | 8 - .../java/org/hisp/dhis/user/UserDetails.java | 9 + .../java/org/hisp/dhis/user/UserService.java | 19 +- .../java/org/hisp/dhis/user/UserStore.java | 2 + .../dhis-services/dhis-service-core/pom.xml | 1 + .../DefaultIdentifiableObjectManager.java | 30 -- .../DefaultOrganisationUnitService.java | 6 - .../java/org/hisp/dhis/query/Criteria.java | 2 +- .../java/org/hisp/dhis/query/Criterion.java | 11 +- .../dhis/query/DefaultJpaQueryParser.java | 88 ++-- .../hisp/dhis/query/DefaultQueryService.java | 32 +- .../hisp/dhis/query/GetObjectListParams.java | 148 +++++++ .../org/hisp/dhis/query/GetObjectParams.java | 130 ++++++ .../dhis/query/JpaCriteriaQueryEngine.java | 152 +++---- .../main/java/org/hisp/dhis/query/Query.java | 4 +- .../java/org/hisp/dhis/query/QueryParser.java | 5 +- .../org/hisp/dhis/query/QueryService.java | 19 +- .../java/org/hisp/dhis/query/Restriction.java | 8 + .../org/hisp/dhis/query/Restrictions.java | 26 ++ .../hisp/dhis/user/DefaultUserService.java | 50 ++- .../user/hibernate/HibernateUserStore.java | 47 ++- .../dhis/query/DefaultQueryServiceTest.java | 2 +- .../dhis/query/GetObjectListParamsTest.java | 120 ++++++ .../DefaultMetadataExportService.java | 47 +-- .../DefaultMetadataExportServiceTest.java | 4 +- .../dhis-services/dhis-service-node/pom.xml | 5 - .../dhis-service-tracker/pom.xml | 4 - .../system/filter/UserRoleCanIssueFilter.java | 55 --- .../message/MessageConversationStoreTest.java | 3 +- .../dhis/query/CriteriaQueryEngineTest.java | 8 +- .../org/hisp/dhis/query/QueryServiceTest.java | 110 ++--- .../AbstractFullReadOnlyControllerTest.java | 2 +- .../OrganisationUnitControllerTest.java | 5 + dhis-2/dhis-web-api/pom.xml | 4 - .../controller/AbstractCrudController.java | 15 +- .../AbstractFullReadOnlyController.java | 322 ++++++--------- .../AggregateDataExchangeController.java | 4 +- .../AnalyticsTableHookController.java | 4 +- .../controller/AttributeController.java | 3 +- .../webapi/controller/ConstantController.java | 3 +- .../controller/DashboardController.java | 3 +- .../controller/DashboardItemController.java | 4 +- .../DataApprovalLevelController.java | 4 +- .../DataApprovalWorkflowController.java | 4 +- .../controller/DataEntryFormController.java | 4 +- .../webapi/controller/DataSetController.java | 3 +- .../webapi/controller/DocumentController.java | 3 +- .../controller/EventFilterController.java | 4 +- .../controller/EventHookController.java | 3 +- .../ExpressionDimensionItemController.java | 3 +- .../controller/FileResourceController.java | 10 +- .../IdentifiableObjectController.java | 7 +- .../controller/InterpretationController.java | 76 +--- .../controller/LegendSetController.java | 3 +- .../MessageConversationController.java | 153 +++---- .../controller/PredictorController.java | 3 +- .../controller/PredictorGroupController.java | 4 +- .../ProgramStageWorkingListController.java | 3 +- .../controller/PushAnalysisController.java | 4 +- .../webapi/controller/ReportController.java | 3 +- .../webapi/controller/RouteController.java | 3 +- .../webapi/controller/SectionController.java | 3 +- .../webapi/controller/SqlViewController.java | 3 +- .../TrackedEntityFilterController.java | 4 +- .../controller/VisualizationController.java | 12 +- .../category/CategoryComboController.java | 4 +- .../category/CategoryController.java | 3 +- .../CategoryOptionComboController.java | 4 +- .../category/CategoryOptionController.java | 4 +- .../CategoryOptionGroupController.java | 4 +- .../CategoryOptionGroupSetController.java | 3 +- .../dataelement/DataElementController.java | 4 +- .../DataElementGroupController.java | 4 +- .../DataElementGroupSetController.java | 4 +- .../DataElementOperandController.java | 140 +++---- .../dimension/DimensionController.java | 128 ++---- .../dimension/DimensionItemPageHandler.java | 9 +- .../event/EventChartController.java | 25 +- .../event/EventReportController.java | 24 +- .../event/EventVisualizationController.java | 9 +- .../controller/event/ProgramController.java | 58 +-- .../event/ProgramDataElementController.java | 99 ++--- .../event/ProgramIndicatorController.java | 4 +- .../ProgramIndicatorGroupController.java | 3 +- .../event/ProgramRuleActionController.java | 4 +- .../event/ProgramRuleController.java | 4 +- .../event/ProgramRuleVariableController.java | 4 +- .../event/ProgramSectionController.java | 4 +- .../event/ProgramStageController.java | 4 +- .../event/ProgramStageSectionController.java | 4 +- .../event/RelationshipTypeController.java | 4 +- .../TrackedEntityAttributeController.java | 37 +- .../event/TrackedEntityTypeController.java | 4 +- .../indicator/IndicatorController.java | 3 +- .../indicator/IndicatorGroupController.java | 4 +- .../IndicatorGroupSetController.java | 4 +- .../indicator/IndicatorTypeController.java | 4 +- .../mapping/ExternalMapLayerController.java | 4 +- .../controller/mapping/MapController.java | 8 +- .../controller/mapping/MapViewController.java | 45 +-- .../message/ProgramMessageController.java | 4 +- ...DataSetNotificationTemplateController.java | 3 +- ...ProgramNotificationTemplateController.java | 3 +- .../controller/option/OptionController.java | 3 +- .../option/OptionGroupController.java | 4 +- .../option/OptionGroupSetController.java | 4 +- .../option/OptionSetController.java | 3 +- .../OrganisationUnitController.java | 377 +++++++----------- .../OrganisationUnitGroupController.java | 3 +- .../OrganisationUnitGroupSetController.java | 3 +- .../OrganisationUnitLevelController.java | 3 +- .../JobConfigurationController.java | 4 +- .../security/ApiTokenController.java | 3 +- .../controller/sms/SmsCommandController.java | 3 +- .../controller/sms/SmsInboundController.java | 11 +- .../controller/sms/SmsOutboundController.java | 12 +- .../controller/user/UserController.java | 189 ++++----- .../controller/user/UserGroupController.java | 3 +- .../controller/user/UserRoleController.java | 41 +- ...idationNotificationTemplateController.java | 3 +- .../validation/ValidationRuleController.java | 53 ++- .../ValidationRuleGroupController.java | 4 +- .../dhis/webapi/openapi/ApiExtractor.java | 21 +- .../dhis/webapi/utils/PaginationUtils.java | 20 +- .../DataElementOperandControllerTest.java | 13 +- .../DimensionItemPageHandlerTest.java | 59 +-- .../webapi/utils/PaginationUtilsTest.java | 20 +- 135 files changed, 1615 insertions(+), 1807 deletions(-) rename dhis-2/{dhis-services/dhis-service-node => dhis-api}/src/main/java/org/hisp/dhis/fieldfilter/Defaults.java (100%) rename dhis-2/{dhis-services/dhis-service-field-filtering => dhis-api}/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java (98%) create mode 100644 dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectListParams.java create mode 100644 dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectParams.java create mode 100644 dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/GetObjectListParamsTest.java delete mode 100644 dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/filter/UserRoleCanIssueFilter.java diff --git a/.github/workflows/run-api-tests.yml b/.github/workflows/run-api-tests.yml index 0e58ab299626..c244585a27f2 100644 --- a/.github/workflows/run-api-tests.yml +++ b/.github/workflows/run-api-tests.yml @@ -41,13 +41,13 @@ jobs: # build and publish multi-arch images using Jib. Image is used for api tests in # this workflow and can be pulled from Dockerhub by devs to run locally, ... mvn clean verify --threads 2C --batch-mode --no-transfer-progress \ - -DskipTests --update-snapshots --file dhis-2/pom.xml \ + -DskipTests -Dmdep.analyze.skip --update-snapshots --file dhis-2/pom.xml \ --projects dhis-web-server --also-make --activate-profiles jibBuild \ -Djib.to.image=$CORE_IMAGE_NAME -Djib.container.labels=DHIS2_BUILD_REVISION=${{github.event.pull_request.head.sha}},DHIS2_BUILD_BRANCH=${{github.head_ref}} else # only build image for running api tests in this workflow i.e. master, 2.39, ... mvn clean verify --threads 2C --batch-mode --no-transfer-progress \ - -DskipTests --update-snapshots --file dhis-2/pom.xml \ + -DskipTests -Dmdep.analyze.skip --update-snapshots --file dhis-2/pom.xml \ --projects dhis-web-server --also-make --activate-profiles jibDockerBuild \ -Djib.to.image=$CORE_IMAGE_NAME fi diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/IdentifiableObjectManager.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/IdentifiableObjectManager.java index f1f4a0f3afdf..12a493210513 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/IdentifiableObjectManager.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/IdentifiableObjectManager.java @@ -182,9 +182,6 @@ List getByCode( @CheckForNull T search(@Nonnull Class type, @Nonnull String query); - @Nonnull - List filter(@Nonnull Class type, @Nonnull String query); - @Nonnull List getAll(@Nonnull Class type); diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/OpenApi.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/OpenApi.java index ccd0dd1ba624..538384551a39 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/OpenApi.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/OpenApi.java @@ -469,6 +469,7 @@ enum Status { */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) + @Inherited @interface Shared { /** diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/collection/CollectionUtils.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/collection/CollectionUtils.java index eba00bea3f3c..4dce993d1163 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/collection/CollectionUtils.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/collection/CollectionUtils.java @@ -214,6 +214,7 @@ private static Set union(@Nonnull Set... more) { * @return a string, or null if no matches. */ public static String popStartsWith(Collection collection, String prefix) { + if (collection == null || collection.isEmpty()) return null; Iterator iterator = collection.iterator(); while (iterator.hasNext()) { diff --git a/dhis-2/dhis-services/dhis-service-node/src/main/java/org/hisp/dhis/fieldfilter/Defaults.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fieldfilter/Defaults.java similarity index 100% rename from dhis-2/dhis-services/dhis-service-node/src/main/java/org/hisp/dhis/fieldfilter/Defaults.java rename to dhis-2/dhis-api/src/main/java/org/hisp/dhis/fieldfilter/Defaults.java diff --git a/dhis-2/dhis-services/dhis-service-field-filtering/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java similarity index 98% rename from dhis-2/dhis-services/dhis-service-field-filtering/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java rename to dhis-2/dhis-api/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java index 8e2dcb7b9099..f257c28c8a0e 100644 --- a/dhis-2/dhis-services/dhis-service-field-filtering/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fieldfiltering/FieldPreset.java @@ -52,7 +52,7 @@ public enum FieldPreset { FieldPreset(String name, List fields) { this.name = name; - this.fields = fields; + this.fields = List.copyOf(fields); } public String getName() { diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/MentionUtils.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/MentionUtils.java index 42110e111df4..c17f9d41be8e 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/MentionUtils.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/MentionUtils.java @@ -31,7 +31,6 @@ import java.util.Date; import java.util.HashSet; import java.util.List; -import java.util.ListIterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -62,17 +61,4 @@ public static Set getMentionedUsers(String text, UserService userService) } return users; } - - public static List removeCustomFilters(List filters) { - List mentions = new ArrayList(); - ListIterator filterIterator = filters.listIterator(); - while (filterIterator.hasNext()) { - String[] filterSplit = filterIterator.next().split(":"); - if (filterSplit[1].equals("in") && filterSplit[0].equals("mentions")) { - mentions.add(filterSplit[2]); - filterIterator.remove(); - } - } - return mentions; - } } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java index 316bbc99e996..c528e5332e9a 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java @@ -41,6 +41,7 @@ import org.apache.commons.lang3.StringUtils; import org.hisp.dhis.common.BaseIdentifiableObject; import org.hisp.dhis.common.DxfNamespaces; +import org.hisp.dhis.common.UID; import org.hisp.dhis.schema.annotation.PropertyRange; import org.hisp.dhis.user.User; @@ -192,12 +193,12 @@ public boolean isRead(User user) { return false; } - public boolean markRead(User user) { - for (UserMessage userMessage : userMessages) { - if (userMessage.getUser() != null && userMessage.getUser().equals(user)) { - boolean read = userMessage.isRead(); + public boolean markRead(UID user) { + for (UserMessage msg : userMessages) { + if (msg.getUser() != null && msg.getUser().getUid().equals(user.getValue())) { + boolean read = msg.isRead(); - userMessage.setRead(true); + msg.setRead(true); return !read; } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java index 3beb007af819..f76dcab600e9 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java @@ -133,14 +133,6 @@ public interface OrganisationUnitService extends OrganisationUnitDataIntegrityPr */ List getOrganisationUnitsByUid(@Nonnull Collection uids); - /** - * Returns a list of OrganisationUnits based on the given params. - * - * @param params the params. - * @return a list of OrganisationUnits. - */ - List getOrganisationUnitsByQuery(OrganisationUnitQueryParams params); - /** * Returns an OrganisationUnit with a given name. * diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java index 3b45942510b6..cca0ccb8ede9 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserDetails.java @@ -28,6 +28,7 @@ package org.hisp.dhis.user; import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -250,6 +251,14 @@ static UserDetails createUserDetails( void setId(Long id); + default boolean canIssueUserRole(UserRole role, boolean canGrantOwnUserRole) { + if (role == null) return false; + if (isSuper()) return true; + if (hasAnyAuthorities(List.of(Authorities.ALL))) return true; + if (!canGrantOwnUserRole && getUserRoleIds().contains(role.getUid())) return false; + return getAllAuthorities().containsAll(role.getAuthorities()); + } + default boolean isInUserHierarchy(String orgUnitPath) { return isInUserHierarchy(orgUnitPath, getUserOrgUnitIds()); } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserService.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserService.java index 9b8b905c2d43..a64c41765021 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserService.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserService.java @@ -43,6 +43,7 @@ import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.UID; import org.hisp.dhis.dataset.DataSet; +import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.feedback.ErrorReport; import org.hisp.dhis.feedback.ForbiddenException; @@ -235,6 +236,17 @@ static boolean hasTwoFactorSecretForApproval(User user) { */ List getUsers(UserQueryParams params, @Nullable List orders); + /** + * Returns a list of users based on the given query parameters. If the specified list of orders + * are empty, default order of last name and first name will be applied. + * + * @param params the user query parameters. + * @param orders the already validated order strings (e.g. email:asc). + * @return a List of users. + */ + List getUserIds(UserQueryParams params, @Nullable List orders) + throws ConflictException; + /** * Returns the number of users based on the given query parameters. * @@ -408,12 +420,9 @@ static boolean hasTwoFactorSecretForApproval(User user) { int countDataSetUserRoles(DataSet dataSet); /** - * Filters the given collection of user roles based on whether the current user is allowed to - * issue it. - * - * @param userRoles the collection of user roles. + * @return IDs of the roles the current user can issue */ - void canIssueFilter(Collection userRoles); + List getRolesCurrentUserCanIssue(); /** * Validate that the current user are allowed to create or modify properties of the given user. diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserStore.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserStore.java index 95faf611f108..f286f26e5850 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserStore.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserStore.java @@ -65,6 +65,8 @@ public interface UserStore extends IdentifiableObjectStore { */ List getUsers(UserQueryParams params, @Nullable List orders); + List getUserIds(UserQueryParams params, @Nullable List orders); + /** * Returns the number of users based on the given query parameters. * diff --git a/dhis-2/dhis-services/dhis-service-core/pom.xml b/dhis-2/dhis-services/dhis-service-core/pom.xml index 86fa7ae4513f..2e76d7dac6b4 100644 --- a/dhis-2/dhis-services/dhis-service-core/pom.xml +++ b/dhis-2/dhis-services/dhis-service-core/pom.xml @@ -30,6 +30,7 @@ org.hisp.dhis dhis-service-node + test org.hisp.dhis diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultIdentifiableObjectManager.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultIdentifiableObjectManager.java index 0ea7377fea9f..cb904f2a4441 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultIdentifiableObjectManager.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultIdentifiableObjectManager.java @@ -39,9 +39,7 @@ import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -409,34 +407,6 @@ public T search(@Nonnull Class type, @Nonnull return object; } - @Nonnull - @Override - @Transactional(readOnly = true) - public List filter( - @Nonnull Class type, @Nonnull String query) { - Set uniqueObjects = new HashSet<>(); - - T uidObject = get(type, query); - - if (uidObject != null) { - uniqueObjects.add(uidObject); - } - - T codeObject = getByCode(type, query); - - if (codeObject != null) { - uniqueObjects.add(codeObject); - } - - uniqueObjects.addAll(getLikeName(type, query, false)); - - List objects = new ArrayList<>(uniqueObjects); - - Collections.sort(objects); - - return objects; - } - @Nonnull @Override @Transactional(readOnly = true) diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java index c1e34b6580bc..280d0e6c2ec2 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java @@ -163,12 +163,6 @@ public List getOrganisationUnitsByUid(@Nonnull Collection(uids)); } - @Override - @Transactional(readOnly = true) - public List getOrganisationUnitsByQuery(OrganisationUnitQueryParams params) { - return organisationUnitStore.getOrganisationUnits(params); - } - @Override @Transactional(readOnly = true) public OrganisationUnit getOrganisationUnit(String uid) { diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criteria.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criteria.java index 5cbe43026ac2..cf3d4b84bdb6 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criteria.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criteria.java @@ -61,7 +61,7 @@ public Criteria add(Criterion... criterions) { return this; } - public Criteria add(Collection criterions) { + public Criteria add(Collection criterions) { this.criterions.addAll(criterions); return this; } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criterion.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criterion.java index 470a6b110825..6e1f47cd0dec 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criterion.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Criterion.java @@ -30,4 +30,13 @@ /** * @author Morten Olav Hansen */ -public interface Criterion {} +public interface Criterion { + + /** + * @return true, when the condition cannot match any rows, e.g. an in-operator with an empty + * collection to test against + */ + default boolean isAlwaysFalse() { + return false; + } +} diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultJpaQueryParser.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultJpaQueryParser.java index 8028a8425667..c405c5cfd989 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultJpaQueryParser.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultJpaQueryParser.java @@ -28,10 +28,15 @@ package org.hisp.dhis.query; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.stream.Collectors.joining; import static org.hisp.dhis.query.QueryUtils.parseValue; +import com.google.common.collect.Lists; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; +import javax.annotation.Nonnull; import org.hisp.dhis.common.CodeGenerator; import org.hisp.dhis.query.operators.MatchMode; import org.hisp.dhis.schema.Property; @@ -52,16 +57,17 @@ public DefaultJpaQueryParser(SchemaService schemaService) { } @Override - public Query parse(Class klass, List filters) throws QueryParserException { + public Query parse(Class klass, @Nonnull List filters) throws QueryParserException { return parse(klass, filters, Junction.Type.AND); } @Override - public Query parse(Class klass, List filters, Junction.Type rootJunction) + public Query parse(Class klass, @Nonnull List filters, Junction.Type rootJunction) throws QueryParserException { Schema schema = schemaService.getDynamicSchema(klass); Query query = Query.from(schema, rootJunction); + List mentions = new ArrayList<>(); for (String filter : filters) { String[] split = filter.split(":"); @@ -69,16 +75,22 @@ public Query parse(Class klass, List filters, Junction.Type rootJunct throw new QueryParserException("Invalid filter => " + filter); } + String path = split[0]; + String operator = split[1]; if (split.length >= 3) { - int index = split[0].length() + ":".length() + split[1].length() + ":".length(); - - if (split[0].equals(IDENTIFIABLE) && !schema.hasProperty(IDENTIFIABLE)) { - handleIdentifiablePath(schema, split[1], filter.substring(index), query.addDisjunction()); + String arg = split.length == 3 ? split[2] : Stream.of(split).skip(2).collect(joining(":")); + if ("mentions".equals(path) && "in".equals(operator)) { + mentions.add(arg); + } else if (path.equals(IDENTIFIABLE) && !schema.hasProperty(IDENTIFIABLE)) { + handleIdentifiablePath(schema, operator, arg, query.addDisjunction()); } else { - query.add(getRestriction(schema, split[0], split[1], filter.substring(index))); + query.add(getRestriction(schema, path, operator, arg)); } } else { - query.add(getRestriction(schema, split[0], split[1], null)); + query.add(getRestriction(schema, path, operator, null)); + } + if (!mentions.isEmpty()) { + getDisjunctionsFromCustomMentions(mentions, query.getSchema()); } } return query; @@ -137,36 +149,8 @@ private Restriction getRestriction( case "endsWith", "ilike$" -> Restrictions.ilike(path, parseValue(valueType, arg), MatchMode.END); case "!ilike$" -> Restrictions.notIlike(path, parseValue(valueType, arg), MatchMode.END); - case "in" -> { - Collection values = null; - - if (property.isCollection()) { - values = parseValue(Collection.class, property.getItemKlass(), arg); - } else { - values = parseValue(Collection.class, valueType, arg); - } - - if (values == null || values.isEmpty()) { - throw new QueryParserException("Invalid argument `" + arg + "` for in operator."); - } - - yield Restrictions.in(path, values); - } - case "!in" -> { - Collection values = null; - - if (property.isCollection()) { - values = parseValue(Collection.class, property.getItemKlass(), arg); - } else { - values = parseValue(Collection.class, valueType, arg); - } - - if (values == null || values.isEmpty()) { - throw new QueryParserException("Invalid argument `" + arg + "` for in operator."); - } - - yield Restrictions.notIn(path, values); - } + case "in" -> Restrictions.in(path, parseValues(property, valueType, arg)); + case "!in" -> Restrictions.notIn(path, parseValues(property, valueType, arg)); case "null" -> Restrictions.isNull(path); case "!null" -> Restrictions.isNotNull(path); case "empty" -> Restrictions.isEmpty(path); @@ -174,6 +158,34 @@ private Restriction getRestriction( }; } + @Nonnull + @SuppressWarnings("rawtypes") + private Collection parseValues(Property property, Class valueType, Object arg) { + Collection values = + property.isCollection() + ? parseValue(Collection.class, property.getItemKlass(), arg) + : parseValue(Collection.class, valueType, arg); + + if (values == null || values.isEmpty()) { + throw new QueryParserException("Invalid argument `" + arg + "` for in operator."); + } + return values; + } + + private Collection getDisjunctionsFromCustomMentions( + List mentions, Schema schema) { + Collection disjunctions = new ArrayList<>(); + for (String m : mentions) { + Disjunction disjunction = new Disjunction(schema); + String[] split = m.substring(1, m.length() - 1).split(","); + List items = Lists.newArrayList(split); + disjunction.add(Restrictions.in("mentions.username", items)); + disjunction.add(Restrictions.in("comments.mentions.username", items)); + disjunctions.add(disjunction); + } + return disjunctions; + } + @Override public Property getProperty(Schema schema, String path) throws QueryParserException { String[] paths = path.split("\\."); diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java index c58e2167162b..da638c64310e 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java @@ -35,6 +35,8 @@ import org.hisp.dhis.preheat.Preheat; import org.hisp.dhis.query.planner.QueryPlan; import org.hisp.dhis.query.planner.QueryPlanner; +import org.hisp.dhis.schema.Schema; +import org.hisp.dhis.schema.SchemaService; import org.springframework.stereotype.Component; /** @@ -49,11 +51,10 @@ public class DefaultQueryService implements QueryService { private static final Junction.Type DEFAULT_JUNCTION_TYPE = Junction.Type.AND; private final QueryParser queryParser; - private final QueryPlanner queryPlanner; + private final SchemaService schemaService; private final JpaCriteriaQueryEngine criteriaQueryEngine; - private final InMemoryQueryEngine inMemoryQueryEngine; @Override @@ -85,23 +86,16 @@ public long count(Query query) { } @Override - public Query getQueryFromUrl( - Class klass, List filters, List orders, Pagination pagination) + public Query getQueryFromUrl(Class type, GetObjectListParams params) throws QueryParserException { - return getQueryFromUrl(klass, filters, orders, pagination, DEFAULT_JUNCTION_TYPE); - } + List filters = params.getFilters(); + if (filters == null) filters = List.of(); + Query query = queryParser.parse(type, filters, params.getRootJunction()); - @Override - public Query getQueryFromUrl( - Class klass, - List filters, - List orders, - Pagination pagination, - Junction.Type rootJunction) - throws QueryParserException { - Query query = queryParser.parse(klass, filters, rootJunction); - query.addOrders(orders); + Schema schema = schemaService.getDynamicSchema(type); + query.addOrders(QueryUtils.convertOrderStrings(params.getOrders(), schema)); + Pagination pagination = params.getPagination(); if (pagination.hasPagination()) { query.setFirstResult(pagination.getFirstResult()); query.setMaxResults(pagination.getSize()); @@ -110,12 +104,6 @@ public Query getQueryFromUrl( return query; } - @Override - public Query getQueryFromUrl(Class klass, List filters, List orders) - throws QueryParserException { - return getQueryFromUrl(klass, filters, orders, new Pagination(), DEFAULT_JUNCTION_TYPE); - } - // --------------------------------------------------------------------------------------------- // Helper methods // --------------------------------------------------------------------------------------------- diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectListParams.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectListParams.java new file mode 100644 index 000000000000..fb440e4aacb8 --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectListParams.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2004-2024, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.query; + +import static java.lang.Math.max; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * Base for parameters supported by CRUD {@code CRUD.getObjectList}. + * + * @author Jan Bernitt + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class GetObjectListParams extends GetObjectParams { + + @JsonProperty("filter") + @CheckForNull + List filters; + + @JsonProperty("order") + @CheckForNull + List orders; + + @JsonProperty Junction.Type rootJunction = Junction.Type.AND; + + @JsonProperty boolean paging = true; + @JsonProperty int page = 1; + @JsonProperty int pageSize = 50; + + /** + * A special filter that matches the query term against the ID and code (equals) and against name + * (contains). Can be used in combination with regular filters. + */ + @JsonProperty String query; + + @Nonnull + @JsonIgnore + public Pagination getPagination() { + if (!paging) return new Pagination(); + // ignore if page < 0 + return new Pagination((max(page, 1) - 1) * pageSize, pageSize); + } + + public GetObjectListParams addFilter(String filter) { + if (filters == null) filters = new ArrayList<>(); + filters.add(filter); + return this; + } + + public GetObjectListParams addOrder(String order) { + if (orders == null) orders = new ArrayList<>(); + orders.add(order); + return this; + } + + /** + * For spring URL parameter injection only. + * + *

The issue is that a filter value may contain comma as part of the value, not to separate + * values which can be misunderstood by spring so the splitting needs to be done with custom + * logic. + * + * @param filters all filters as a comma seperated string + */ + public void setFilter(String filters) { + this.filters = splitFilters(filters); + } + + public void setOrder(List orders) { + this.orders = orders; + } + + /** + * Pattern to split {@code filter} expressions which have 2 or 3 components: {@code + * property:operator} or {@code property:operator:value}. + * + *

Examples + * + *

+   * name:eq:Peter
+   * organisationUnits:empty
+   * 
+ * + * Unfortunately the value can also contain : and when used with [] it can also contain , which + * needs extra caution to not read too little or too much into operation and value. + */ + private static final Pattern FILTER_PARTS = + Pattern.compile( + "(?[a-zA-Z0-9.]+):(?[!$a-zA-Z]{2,10})(?::(?\\[[^]]*]|[^,\\n\\r]+))?"); + + /** + * Splits a string {@code filter,filter} into a list {@code [filter,filter]}. + * + *

This is non-trivial since filters can contain comma symbols. + * + * @param filters a comma seperated list of filter expressions + * @return a list where each element is a single filter expression + */ + @CheckForNull + static List splitFilters(@CheckForNull String filters) { + if (filters == null || filters.isEmpty()) return null; + if (!filters.contains(",")) return new ArrayList<>(List.of(filters)); + List res = new ArrayList<>(); + Matcher m = FILTER_PARTS.matcher(filters); + while (m.find()) { + res.add(m.group()); + } + return res; + } +} diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectParams.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectParams.java new file mode 100644 index 000000000000..484ca06890b1 --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/GetObjectParams.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2004-2024, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.query; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.fieldfilter.Defaults; +import org.hisp.dhis.fieldfiltering.FieldPreset; + +/** + * Base for parameters supported by CRUD {@code CRUD.getObject}. + * + * @author Jan Bernitt + */ +@Getter +@EqualsAndHashCode +@ToString +@Accessors(chain = true) +@OpenApi.Shared +public class GetObjectParams { + + /** + * Fields allow {@code property[sub,sub]} syntax where a comma occurs as part of the property + * name. These commas need to be ignored when splitting a {@code fields} parameter list. + */ + private static final String FIELD_SPLIT = ",(?![^\\[\\]]*\\]|[^\\(\\)]*\\)|([a-zA-Z0-9]+,?)+\\))"; + + @OpenApi.Shared.Inline + @OpenApi.Property(OpenApi.PropertyNames[].class) + @JsonProperty + @CheckForNull + List fields; + + @Setter @JsonProperty @Nonnull Defaults defaults = Defaults.INCLUDE; + + /** + * Since splitting on comma is too unsophisticated to be correct this needs custom split code when + * called by spring injecting URL parmaeters + * + * @param fields field expression + */ + public void setFields(String fields) { + this.fields = fields == null ? null : List.of(fields.split(FIELD_SPLIT)); + } + + public GetObjectParams addField(String field) { + if (fields == null) fields = new ArrayList<>(); + fields.add(field); + return this; + } + + /** + * @return list of fields with defaults as used when viewing a single object + */ + @Nonnull + @JsonIgnore + public List getFieldsObject() { + if (fields == null || fields.isEmpty()) return List.of("*"); + return fields; + } + + /** + * @return list of fields with defaults as used when viewing a list as JSON + */ + @Nonnull + @JsonIgnore + public List getFieldsJsonList() { + if (fields == null || fields.isEmpty()) return FieldPreset.defaultPreset().getFields(); + return fields; + } + + /** + * @return list of fields with defaults as used when viewing a list as CSV + */ + @Nonnull + @JsonIgnore + public List getFieldsCsvList() { + List res = new ArrayList<>(); + if (fields != null) res.addAll(fields); + if (res.isEmpty() || res.contains("*") || res.contains(":all")) + res.addAll(FieldPreset.defaultPreset().getFields()); + return res; + } + + /** + * @return elevates a single object params object to a list params object as a way to adapt to the + * list based query engine that is also used for single object lookup + */ + public GetObjectListParams toListParams() { + GetObjectListParams res = new GetObjectListParams(); + if (fields != null) fields.forEach(res::addField); + res.setDefaults(defaults); + return res; + } +} diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/JpaCriteriaQueryEngine.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/JpaCriteriaQueryEngine.java index ab03313c93f4..9f9a02b251c7 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/JpaCriteriaQueryEngine.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/JpaCriteriaQueryEngine.java @@ -28,6 +28,7 @@ package org.hisp.dhis.query; import static com.google.common.base.Preconditions.checkNotNull; +import static org.hisp.dhis.user.CurrentUserUtil.getCurrentUserDetails; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; @@ -39,6 +40,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import javax.annotation.Nonnull; import org.hibernate.jpa.QueryHints; import org.hisp.dhis.cache.QueryCacheManager; import org.hisp.dhis.common.IdentifiableObject; @@ -47,7 +50,6 @@ import org.hisp.dhis.query.planner.QueryPlan; import org.hisp.dhis.query.planner.QueryPlanner; import org.hisp.dhis.schema.Schema; -import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.UserDetails; import org.springframework.stereotype.Component; @@ -64,7 +66,7 @@ public class JpaCriteriaQueryEngine implements Que private final QueryCacheManager queryCacheManager; - private Map, IdentifiableObjectStore> stores = new HashMap<>(); + private final Map, IdentifiableObjectStore> stores = new HashMap<>(); public JpaCriteriaQueryEngine( QueryPlanner queryPlanner, @@ -86,16 +88,17 @@ public JpaCriteriaQueryEngine( public List query(Query query) { Schema schema = query.getSchema(); + @SuppressWarnings("unchecked") Class klass = (Class) schema.getKlass(); - InternalHibernateGenericStore store = (InternalHibernateGenericStore) getStore(klass); + InternalHibernateGenericStore store = getStore(klass); if (store == null) { return new ArrayList<>(); } if (query.getCurrentUserDetails() == null) { - query.setCurrentUserDetails(CurrentUserUtil.getCurrentUserDetails()); + query.setCurrentUserDetails(getCurrentUserDetails()); } if (!query.isPlannedQuery()) { @@ -108,68 +111,11 @@ public List query(Query query) { CriteriaQuery criteriaQuery = builder.createQuery(klass); Root root = criteriaQuery.from(klass); - if (query.isEmpty()) { - - UserDetails userDetails = - query.getCurrentUserDetails() != null - ? query.getCurrentUserDetails() - : CurrentUserUtil.getCurrentUserDetails(); - - Predicate predicate = builder.conjunction(); - boolean shareable = schema.isShareable(); - if (shareable && !query.isSkipSharing()) { - predicate - .getExpressions() - .addAll( - store.getSharingPredicates(builder, userDetails).stream() - .map(t -> t.apply(root)) - .toList()); - } - criteriaQuery.where(predicate); - - TypedQuery typedQuery = entityManager.createQuery(criteriaQuery); - - typedQuery.setFirstResult(query.getFirstResult()); - typedQuery.setMaxResults(query.getMaxResults()); - - return typedQuery.getResultList(); - } - Predicate predicate = buildPredicates(builder, root, query); - boolean shareable = schema.isShareable(); - - String username = - CurrentUserUtil.getCurrentUsername() != null - ? CurrentUserUtil.getCurrentUsername() - : "system-process"; - - if (!username.equals("system-process") && shareable && !query.isSkipSharing()) { - - UserDetails userDetails = - query.getCurrentUserDetails() != null - ? query.getCurrentUserDetails() - : CurrentUserUtil.getCurrentUserDetails(); - - predicate - .getExpressions() - .addAll( - store.getSharingPredicates(builder, userDetails).stream() - .map(t -> t.apply(root)) - .toList()); - } - + addSharingPredicates(query, schema, predicate, store, builder, root); criteriaQuery.where(predicate); - if (!query.getOrders().isEmpty()) { - criteriaQuery.orderBy( - query.getOrders().stream() - .map( - o -> - o.isAscending() - ? builder.asc(root.get(o.getProperty().getFieldName())) - : builder.desc(root.get(o.getProperty().getFieldName()))) - .toList()); - } + if (!query.getOrders().isEmpty()) criteriaQuery.orderBy(getOrders(query, builder, root)); TypedQuery typedQuery = entityManager.createQuery(criteriaQuery); @@ -186,20 +132,42 @@ public List query(Query query) { return typedQuery.getResultList(); } + private static void addSharingPredicates( + Query query, + Schema schema, + Predicate predicate, + InternalHibernateGenericStore store, + CriteriaBuilder builder, + Root root) { + boolean shareable = schema.isShareable(); + if (!shareable) return; + UserDetails user = query.getCurrentUserDetails(); + if (user == null) user = getCurrentUserDetails(); + List, Predicate>> predicates = List.of(); + if (query.isDataSharing()) { + predicates = store.getDataSharingPredicates(builder, user); + } else if (!query.isSkipSharing()) { + predicates = store.getSharingPredicates(builder, user); + } + if (!predicates.isEmpty()) + predicate.getExpressions().addAll(predicates.stream().map(t -> t.apply(root)).toList()); + } + @Override public long count(Query query) { Schema schema = query.getSchema(); + @SuppressWarnings("unchecked") Class klass = (Class) schema.getKlass(); - InternalHibernateGenericStore store = (InternalHibernateGenericStore) getStore(klass); + InternalHibernateGenericStore store = getStore(klass); if (store == null) { return 0; } if (query.getCurrentUserDetails() == null) { - query.setCurrentUserDetails(CurrentUserUtil.getCurrentUserDetails()); + query.setCurrentUserDetails(getCurrentUserDetails()); } if (!query.isPlannedQuery()) { @@ -215,36 +183,26 @@ public long count(Query query) { criteriaQuery.select(builder.count(root)); Predicate predicate = buildPredicates(builder, root, query); - - boolean shareable = schema.isShareable(); - if (shareable && !query.isSkipSharing()) { - UserDetails currentUserDetails = query.getCurrentUserDetails(); - predicate - .getExpressions() - .addAll( - store.getSharingPredicates(builder, currentUserDetails).stream() - .map(t -> t.apply(root)) - .toList()); - } - + addSharingPredicates(query, schema, predicate, store, builder, root); criteriaQuery.where(predicate); - if (!query.getOrders().isEmpty()) { - criteriaQuery.orderBy( - query.getOrders().stream() - .map( - o -> - o.isAscending() - ? builder.asc(root.get(o.getProperty().getName())) - : builder.desc(root.get(o.getProperty().getName()))) - .toList()); - } - TypedQuery typedQuery = entityManager.createQuery(criteriaQuery); return typedQuery.getSingleResult(); } + @Nonnull + private static List getOrders( + Query query, CriteriaBuilder builder, Root root) { + return query.getOrders().stream() + .map( + o -> + o.isAscending() + ? builder.asc(root.get(o.getProperty().getFieldName())) + : builder.desc(root.get(o.getProperty().getFieldName()))) + .toList(); + } + private void initStoreMap() { if (!stores.isEmpty()) { return; @@ -255,13 +213,15 @@ private void initStoreMap() { } } - private IdentifiableObjectStore getStore(Class klass) { + @SuppressWarnings("unchecked") + private InternalHibernateGenericStore getStore(Class klass) { initStoreMap(); - return stores.get(klass); + return (InternalHibernateGenericStore) stores.get(klass); } private Predicate buildPredicates(CriteriaBuilder builder, Root root, Query query) { Predicate junction = builder.conjunction(); + if (query.isEmpty()) return junction; query.getAliases().forEach(alias -> root.join(alias).alias(alias)); if (!query.getCriterions().isEmpty()) { junction = getJpaJunction(builder, query.getRootJunctionType()); @@ -274,14 +234,10 @@ private Predicate buildPredicates(CriteriaBuilder builder, Root root, Que } private Predicate getJpaJunction(CriteriaBuilder builder, Junction.Type type) { - switch (type) { - case AND: - return builder.conjunction(); - case OR: - return builder.disjunction(); - } - - return builder.conjunction(); + return switch (type) { + case AND -> builder.conjunction(); + case OR -> builder.disjunction(); + }; } private Predicate getPredicate( diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java index 564eb9dbef5b..0c3e514539d9 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java @@ -58,6 +58,8 @@ public class Query extends Criteria { private boolean skipSharing; + private boolean dataSharing; + private Integer firstResult = 0; private Integer maxResults = Integer.MAX_VALUE; @@ -150,7 +152,7 @@ public Query add(Criterion... criterions) { } @Override - public Query add(Collection criterions) { + public Query add(Collection criterions) { super.add(criterions); return this; } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryParser.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryParser.java index 5cf2f4d96cb0..69547997e708 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryParser.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryParser.java @@ -28,6 +28,7 @@ package org.hisp.dhis.query; import java.util.List; +import javax.annotation.Nonnull; import org.hisp.dhis.schema.Property; import org.hisp.dhis.schema.Schema; @@ -48,10 +49,10 @@ public interface QueryParser { * @return Query instance based on Schema of klass and filters list * @throws QueryParserException */ - Query parse(Class klass, List filters, Junction.Type rootJunction) + Query parse(Class klass, @Nonnull List filters, Junction.Type rootJunction) throws QueryParserException; - Query parse(Class klass, List filters) throws QueryParserException; + Query parse(Class klass, @Nonnull List filters) throws QueryParserException; Property getProperty(Schema schema, String path) throws QueryParserException; } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryService.java index d48849d10c6b..816cee399409 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/QueryService.java @@ -65,23 +65,8 @@ public interface QueryService { * orders. * * @param klass Type of object you want to query - * @param filters List of filters to use as basis for query instance - * @param orders List of orders to use for query - * @param rootJunction Root junction (defaults to AND) + * @param params standard object list query parameters * @return New query instance using provided filters/orders */ - Query getQueryFromUrl( - Class klass, - List filters, - List orders, - Pagination pagination, - Junction.Type rootJunction) - throws QueryParserException; - - Query getQueryFromUrl( - Class klass, List filters, List orders, Pagination pagination) - throws QueryParserException; - - Query getQueryFromUrl(Class klass, List filters, List orders) - throws QueryParserException; + Query getQueryFromUrl(Class klass, GetObjectListParams params) throws QueryParserException; } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restriction.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restriction.java index 210ae7c2cd32..5ebf3c95ff3e 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restriction.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restriction.java @@ -32,6 +32,7 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import org.hisp.dhis.query.operators.InOperator; import org.hisp.dhis.query.operators.Operator; import org.hisp.dhis.query.planner.QueryPath; @@ -71,4 +72,11 @@ public Restriction asAttribute() { public String toString() { return "[" + path + ", op: " + operator + "]"; } + + @Override + public boolean isAlwaysFalse() { + if (operator instanceof InOperator in) + return in.getCollectionArgs().isEmpty() || in.getCollectionArgs().get(0).isEmpty(); + return false; + } } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restrictions.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restrictions.java index 1f3249b0612f..48bb0af8cd4b 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restrictions.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Restrictions.java @@ -28,6 +28,7 @@ package org.hisp.dhis.query; import java.util.Collection; +import java.util.List; import org.hisp.dhis.query.operators.BetweenOperator; import org.hisp.dhis.query.operators.EmptyOperator; import org.hisp.dhis.query.operators.EqualOperator; @@ -45,6 +46,7 @@ import org.hisp.dhis.query.operators.NotTokenOperator; import org.hisp.dhis.query.operators.NullOperator; import org.hisp.dhis.query.operators.TokenOperator; +import org.hisp.dhis.schema.Schema; /** * @author Morten Olav Hansen @@ -131,5 +133,29 @@ public static Restriction isEmpty(String path) { return new Restriction(path, new EmptyOperator<>()); } + public static Disjunction query(Schema schema, String query) { + return or(schema, eq("id", query), eq("code", query), ilike("name", query, MatchMode.ANYWHERE)); + } + + public static Disjunction or(Schema schema, Criterion... filters) { + return or(schema, List.of(filters)); + } + + public static Disjunction or(Schema schema, List filters) { + Disjunction or = new Disjunction(schema); + or.add(filters); + return or; + } + + public static Conjunction and(Schema schema, Criterion... filters) { + return and(schema, List.of(filters)); + } + + public static Conjunction and(Schema schema, List filters) { + Conjunction and = new Conjunction(schema); + and.add(filters); + return and; + } + private Restrictions() {} } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java index e2eeef2a4af7..6409343033a1 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java @@ -70,9 +70,9 @@ import org.hisp.dhis.common.PasswordGenerator; import org.hisp.dhis.common.UID; import org.hisp.dhis.common.UserOrgUnitType; -import org.hisp.dhis.commons.filter.FilterUtils; import org.hisp.dhis.dataset.DataSet; import org.hisp.dhis.email.EmailResponse; +import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.feedback.ErrorReport; import org.hisp.dhis.feedback.ForbiddenException; @@ -89,7 +89,6 @@ import org.hisp.dhis.security.TwoFactoryAuthenticationUtils; import org.hisp.dhis.security.acl.AclService; import org.hisp.dhis.setting.SystemSettingsProvider; -import org.hisp.dhis.system.filter.UserRoleCanIssueFilter; import org.hisp.dhis.system.util.ValidationUtils; import org.hisp.dhis.system.velocity.VelocityManager; import org.hisp.dhis.util.DateUtils; @@ -314,19 +313,35 @@ public List getUsers(UserQueryParams params) { public List getUsers(UserQueryParams params, @Nullable List orders) { handleUserQueryParams(params); - if (isNotValidUserQueryParams(params)) { + try { + validateUserQueryParams(params); + } catch (ConflictException ex) { + log.warn(ex.getMessage()); return Lists.newArrayList(); } return userStore.getUsers(params, orders); } + @Override + @Transactional(readOnly = true) + public List getUserIds(UserQueryParams params, @CheckForNull List orders) + throws ConflictException { + handleUserQueryParams(params); + validateUserQueryParams(params); + + return userStore.getUserIds(params, orders); + } + @Override @Transactional(readOnly = true) public int getUserCount(UserQueryParams params) { handleUserQueryParams(params); - if (isNotValidUserQueryParams(params)) { + try { + validateUserQueryParams(params); + } catch (ConflictException ex) { + log.warn(ex.getMessage()); return 0; } @@ -382,25 +397,21 @@ private void handleUserQueryParams(UserQueryParams params) { } } - private boolean isNotValidUserQueryParams(UserQueryParams params) { + private void validateUserQueryParams(UserQueryParams params) throws ConflictException { if (params.isCanManage() && (params.getUser() == null || !params.getUser().hasManagedGroups())) { - log.warn("Cannot get managed users as user does not have any managed groups"); - return true; + throw new ConflictException( + "Cannot get managed users as user does not have any managed groups"); } - if (params.isAuthSubset() && (params.getUser() == null || !params.getUser().hasAuthorities())) { - log.warn("Cannot get users with authority subset as user does not have any authorities"); - return true; + throw new ConflictException( + "Cannot get users with authority subset as user does not have any authorities"); } - if (params.isDisjointRoles() && (params.getUser() == null || !params.getUser().hasUserRoles())) { - log.warn("Cannot get users with disjoint roles as user does not have any user roles"); - return true; + throw new ConflictException( + "Cannot get users with disjoint roles as user does not have any user roles"); } - - return false; } @Override @@ -545,12 +556,15 @@ public int countDataSetUserRoles(DataSet dataSet) { @Override @Transactional(readOnly = true) - public void canIssueFilter(Collection userRoles) { - User user = getUserByUsername(CurrentUserUtil.getCurrentUsername()); + public List getRolesCurrentUserCanIssue() { + UserDetails user = CurrentUserUtil.getCurrentUserDetails(); boolean canGrantOwnUserRoles = settingsProvider.getCurrentSettings().getCanGrantOwnUserRoles(); - FilterUtils.filter(userRoles, new UserRoleCanIssueFilter(user, canGrantOwnUserRoles)); + return userRoleStore.getAll().stream() + .filter(role -> user.canIssueUserRole(role, canGrantOwnUserRoles)) + .map(role -> UID.of(role.getUid())) + .toList(); } @Override diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/hibernate/HibernateUserStore.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/hibernate/HibernateUserStore.java index b1ad62fc4f05..688c3a76c70f 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/hibernate/HibernateUserStore.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/hibernate/HibernateUserStore.java @@ -47,12 +47,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -125,7 +122,7 @@ public void save(@Nonnull User user, boolean clearSharing) { @Override public List getUsers(UserQueryParams params, @Nullable List orders) { - Query userQuery = getUserQuery(params, orders, false); + Query userQuery = getUserQuery(params, orders, QueryMode.OBJECTS); return extractUserQueryUsers(userQuery.list()); } @@ -136,7 +133,7 @@ public List getUsers(UserQueryParams params) { @Override public List getExpiringUsers(UserQueryParams params) { - return extractUserQueryUsers(getUserQuery(params, null, false).list()); + return extractUserQueryUsers(getUserQuery(params, null, QueryMode.OBJECTS).list()); } @Override @@ -155,10 +152,17 @@ public List getExpiringUserAccounts(int inDays) { @Override public int getUserCount(UserQueryParams params) { - Long count = (Long) getUserQuery(params, null, true).uniqueResult(); + Long count = (Long) getUserQuery(params, null, QueryMode.COUNT).uniqueResult(); return count != null ? count.intValue() : 0; } + @Override + public List getUserIds(UserQueryParams params, @CheckForNull List orders) { + return getUserQuery(params, orders, QueryMode.IDS).stream() + .map(id -> UID.of((String) id)) + .toList(); + } + @Nonnull private List extractUserQueryUsers(@Nonnull List result) { if (result.isEmpty()) { @@ -176,30 +180,35 @@ private List extractUserQueryUsers(@Nonnull List result) { return users; } - private Query getUserQuery(UserQueryParams params, List orders, boolean count) { + private enum QueryMode { + OBJECTS, + COUNT, + IDS + } + + private Query getUserQuery(UserQueryParams params, List orders, QueryMode mode) { SqlHelper hlp = new SqlHelper(); List convertedOrder = null; - String hql = null; + String hql; - if (count) { + boolean fetch = mode == QueryMode.OBJECTS; + if (mode == QueryMode.COUNT) { hql = "select count(distinct u) "; + } else if (mode == QueryMode.IDS) { + hql = "select distinct u.uid "; } else { Schema userSchema = schemaService.getSchema(User.class); convertedOrder = QueryUtils.convertOrderStrings(orders, userSchema); - - hql = - Stream.of( - "select distinct u", - JpaQueryUtils.createSelectOrderExpression(convertedOrder, "u")) - .filter(Objects::nonNull) - .collect(Collectors.joining(",")); + String order = JpaQueryUtils.createSelectOrderExpression(convertedOrder, "u"); + hql = "select distinct u"; + if (order != null) hql += "," + order; hql += " "; } hql += "from User u "; - if (params.isPrefetchUserGroups() && !count) { + if (params.isPrefetchUserGroups() && fetch) { hql += "left join fetch u.groups g "; } else { hql += "left join u.groups g "; @@ -308,7 +317,7 @@ private Query getUserQuery(UserQueryParams params, List orders, boole + "and u.restoreExpiry < current_timestamp() "; } - if (!count) { + if (fetch) { String orderExpression = JpaQueryUtils.createOrderExpression(convertedOrder, "u"); hql += "order by " + StringUtils.defaultString(orderExpression, "u.surname, u.firstName"); } @@ -385,7 +394,7 @@ private Query getUserQuery(UserQueryParams params, List orders, boole query.setParameterList("userGroupIds", userGroupIds); } - if (!count) { + if (fetch) { if (params.getFirst() != null) { query.setFirstResult(params.getFirst()); } diff --git a/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/DefaultQueryServiceTest.java b/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/DefaultQueryServiceTest.java index 299d2fde1de6..bb84e91d776f 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/DefaultQueryServiceTest.java +++ b/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/DefaultQueryServiceTest.java @@ -73,7 +73,7 @@ public void setUp() { QueryPlanner queryPlanner = new DefaultQueryPlanner(schemaService, settingsService); subject = new DefaultQueryService( - queryParser, queryPlanner, criteriaQueryEngine, inMemoryQueryEngine); + queryParser, queryPlanner, schemaService, criteriaQueryEngine, inMemoryQueryEngine); } @Test diff --git a/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/GetObjectListParamsTest.java b/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/GetObjectListParamsTest.java new file mode 100644 index 000000000000..69ba2eac6088 --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/GetObjectListParamsTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2004-2024, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Tests the parameter processing implemented in {@link GetObjectListParams}. + * + * @author Jan Bernitt + */ +class GetObjectListParamsTest { + + @Test + void testSplitFilters_empty() { + assertFilters(null, ""); + assertFilters(null, null); + } + + @Test + void testSplitFilters_2parts() { + assertFilters(List.of("name:empty"), "name:empty"); + assertFilters(List.of("organisationUnits:!empty"), "organisationUnits:!empty"); + assertFilters( + List.of("name:empty", "organisationUnits:!empty"), "name:empty,organisationUnits:!empty"); + } + + @Test + void testSplitFilters_3parts() { + assertFilters(List.of("name:eq:Peter"), "name:eq:Peter"); + assertFilters(List.of("parent.id:eq:a1234t6u8o"), "parent.id:eq:a1234t6u8o"); + assertFilters( + List.of("name:eq:Peter", "parent.id:eq:a1234t6u8o"), + "name:eq:Peter,parent.id:eq:a1234t6u8o"); + } + + @Test + void testSplitFilters_in() { + assertFilters(List.of("name:in:[Peter,Paul,Mary]"), "name:in:[Peter,Paul,Mary]"); + assertFilters( + List.of("age:eq:20", "name:in:[Peter,Paul,Mary]", "points:lt:200"), + "age:eq:20,name:in:[Peter,Paul,Mary],points:lt:200"); + } + + @Test + void testSplitFilters_allOperators() { + List ops = + List.of( + "eq", + "ieq", + "!eq", + "neq", + "ne", + "gt", + "lt", + "gte", + "ge", + "lte", + "le", + "like", + "!like", + "$like", + "!$like", + "like$", + "!like$", + "ilike", + "!ilike", + "startsWith", + "$ilike", + "!$ilike", + "token", + "!token", + "endsWith", + "ilkike$", + "!ilike$", + "in", + "!in", + "null", + "empty", + "!null"); + for (String op : ops) { + String filter = "name:%s:value".formatted(op); + assertFilters(List.of(filter), filter); + assertFilters(List.of("foo:eq:bar", filter), "foo:eq:bar," + filter); + assertFilters(List.of("foo:eq:bar", filter, "x:empty"), "foo:eq:bar," + filter + ",x:empty"); + } + } + + private static void assertFilters(List expected, String actual) { + assertEquals(expected, GetObjectListParams.splitFilters(actual)); + } +} diff --git a/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportService.java b/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportService.java index 31f7ffd44176..6a445daafc06 100644 --- a/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportService.java +++ b/dhis-2/dhis-services/dhis-service-dxf2/src/main/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportService.java @@ -36,7 +36,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.Sets; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; @@ -69,7 +68,6 @@ import org.hisp.dhis.dataset.DataSetElement; import org.hisp.dhis.dataset.Section; import org.hisp.dhis.document.Document; -import org.hisp.dhis.dxf2.common.OrderParams; import org.hisp.dhis.eventchart.EventChart; import org.hisp.dhis.eventreport.EventReport; import org.hisp.dhis.eventvisualization.EventVisualization; @@ -97,6 +95,7 @@ import org.hisp.dhis.programrule.ProgramRuleService; import org.hisp.dhis.programrule.ProgramRuleVariable; import org.hisp.dhis.programrule.ProgramRuleVariableService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.query.QueryService; import org.hisp.dhis.report.Report; @@ -177,12 +176,12 @@ public Map, List> parameter for (Map.Entry, Map>> entry : map.entrySet()) { Map> classMap = entry.getValue(); - Schema schema = schemaService.getDynamicSchema(entry.getKey()); - - if (classMap.containsKey("fields")) params.addFields(entry.getKey(), classMap.get("fields")); - - if (classMap.containsKey("filter") && classMap.containsKey("order")) { - OrderParams orderParams = new OrderParams(Sets.newHashSet(classMap.get("order"))); - Query query = - queryService.getQueryFromUrl( - entry.getKey(), classMap.get("filter"), orderParams.getOrders(schema)); - query.setDefaultOrder(); - params.addQuery(query); - } else if (classMap.containsKey("filter")) { - Query query = - queryService.getQueryFromUrl(entry.getKey(), classMap.get("filter"), new ArrayList<>()); - query.setDefaultOrder(); - params.addQuery(query); - } else if (classMap.containsKey("order")) { - OrderParams orderParams = new OrderParams(); - orderParams.setOrder(Sets.newHashSet(classMap.get("order"))); - - Query query = - queryService.getQueryFromUrl( - entry.getKey(), new ArrayList<>(), orderParams.getOrders(schema)); + Class objectType = entry.getKey(); + if (classMap.containsKey("fields")) params.addFields(objectType, classMap.get("fields")); + + List filters = classMap.get("filter"); + List orders = classMap.get("order"); + if (filters != null || orders != null) { + GetObjectListParams queryParams = + new GetObjectListParams().setPaging(false).setFilters(filters).setOrders(orders); + Query query = queryService.getQueryFromUrl(objectType, queryParams); query.setDefaultOrder(); params.addQuery(query); } diff --git a/dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportServiceTest.java b/dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportServiceTest.java index 376ddc884e9d..2901c0842942 100644 --- a/dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportServiceTest.java +++ b/dhis-2/dhis-services/dhis-service-dxf2/src/test/java/org/hisp/dhis/dxf2/metadata/DefaultMetadataExportServiceTest.java @@ -64,6 +64,7 @@ import org.hisp.dhis.scheduling.JobConfiguration; import org.hisp.dhis.schema.Schema; import org.hisp.dhis.schema.SchemaService; +import org.hisp.dhis.user.SystemUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -214,12 +215,13 @@ void deprecatedAnalyticClassesSchemaRemovedTest() { schemas.forEach(s -> s.setPersisted(true)); Query query = Query.from(new Schema(EventReport.class, "eventReport", "eventReports")); - when(queryService.getQueryFromUrl(any(), any(), any())).thenReturn(query); + when(queryService.getQueryFromUrl(any(), any())).thenReturn(query); // return 5 schemas, including the 2 for the deprecated classes EventChart & EventReport when(schemaService.getMetadataSchemas()).thenReturn(schemas); // when + params.setCurrentUserDetails(new SystemUser()); service.getMetadata(params); // then diff --git a/dhis-2/dhis-services/dhis-service-node/pom.xml b/dhis-2/dhis-services/dhis-service-node/pom.xml index 5e75d0c0aa15..e3e72dfbabaa 100644 --- a/dhis-2/dhis-services/dhis-service-node/pom.xml +++ b/dhis-2/dhis-services/dhis-service-node/pom.xml @@ -127,10 +127,5 @@ mockito-junit-jupiter test - - org.hisp.dhis - dhis-service-field-filtering - - diff --git a/dhis-2/dhis-services/dhis-service-tracker/pom.xml b/dhis-2/dhis-services/dhis-service-tracker/pom.xml index 40847583dc15..e4067be79ef4 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/pom.xml +++ b/dhis-2/dhis-services/dhis-service-tracker/pom.xml @@ -33,10 +33,6 @@ org.hisp.dhis dhis-service-acl - - org.hisp.dhis - dhis-service-node - org.hisp.dhis dhis-support-artemis diff --git a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/filter/UserRoleCanIssueFilter.java b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/filter/UserRoleCanIssueFilter.java deleted file mode 100644 index d9cab6377453..000000000000 --- a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/filter/UserRoleCanIssueFilter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.system.filter; - -import org.hisp.dhis.commons.filter.Filter; -import org.hisp.dhis.user.User; -import org.hisp.dhis.user.UserRole; - -/** - * @author Lars Helge Overland - */ -public class UserRoleCanIssueFilter implements Filter { - private User user; - - private boolean canGrantOwnUserRoles = false; - - protected UserRoleCanIssueFilter() {} - - public UserRoleCanIssueFilter(User user, boolean canGrantOwnUserRoles) { - if (user != null) { - this.user = user; - this.canGrantOwnUserRoles = canGrantOwnUserRoles; - } - } - - @Override - public boolean retain(UserRole group) { - return user != null && user.canIssueUserRole(group, canGrantOwnUserRoles); - } -} diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/message/MessageConversationStoreTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/message/MessageConversationStoreTest.java index c741e08ab594..1348f2e5ad44 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/message/MessageConversationStoreTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/message/MessageConversationStoreTest.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.hisp.dhis.common.UID; import org.hisp.dhis.test.integration.PostgresIntegrationTestBase; import org.hisp.dhis.user.User; import org.junit.jupiter.api.BeforeAll; @@ -76,7 +77,7 @@ void setUp() { conversationIds = new HashSet<>(); conversationA = messageService.sendPrivateMessage(usersA, "Subject1", "Text", "Meta", null); MessageConversation mc = messageService.getMessageConversation(conversationA); - mc.markRead(userC); + mc.markRead(UID.of(userC.getUid())); messageService.updateMessageConversation(mc); conversationIds.add(mc.getUid()); messageService.sendReply(mc, "Message 1", "Meta", false, null); diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/CriteriaQueryEngineTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/CriteriaQueryEngineTest.java index 5ea5a8be4a4a..c31bf545a0fd 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/CriteriaQueryEngineTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/CriteriaQueryEngineTest.java @@ -27,8 +27,6 @@ */ package org.hisp.dhis.query; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -509,9 +507,9 @@ void testIdentifiableSearch7() { @Test void testUnicodeSearch() { addDataElement('U', "Кириллица", ValueType.NUMBER, "2021"); - Query query = - queryService.getQueryFromUrl( - DataElement.class, singletonList("identifiable:token:Кири"), emptyList()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("identifiable:token:Кири")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List matches = queryService.query(query); assertEquals(1, matches.size()); assertEquals("Кириллица", matches.get(0).getName()); diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/QueryServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/QueryServiceTest.java index bca5b662cc5f..8a401834d99c 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/QueryServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/query/QueryServiceTest.java @@ -119,9 +119,8 @@ void getAllQuery() { @Test void getAllQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, Lists.newArrayList(), Lists.newArrayList(), new Pagination()); + GetObjectListParams params = new GetObjectListParams().setPaging(false); + Query query = queryService.getQueryFromUrl(DataElement.class, params); assertEquals(6, queryService.query(query).size()); } @@ -147,10 +146,8 @@ void getAllQueryUrlWithPaginationReturnsNoDataWhenPageNumberHigherThanMaxNumberO } private List getResultWithPagination(int page, int pageSize) { - Pagination pagination = new Pagination(page, pageSize); - Query query = - queryService.getQueryFromUrl( - DataElement.class, Lists.newArrayList(), Lists.newArrayList(), pagination); + GetObjectListParams params = new GetObjectListParams().setPage(page).setPageSize(pageSize); + Query query = queryService.getQueryFromUrl(DataElement.class, params); return queryService.query(query); } @@ -177,12 +174,9 @@ void getEqQuery() { @Test void getEqQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("id:eq:deabcdefghA"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("id:eq:deabcdefghA")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(1, objects.size()); assertEquals("deabcdefghA", objects.get(0).getUid()); @@ -233,12 +227,9 @@ void getIlikeQueryNoMatchExtraCharAtStart() { @Test void getIeqQueryUrlMatch() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("name:ieq:dataelementa"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("name:ieq:dataelementa")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(1, objects.size()); assertEquals("DataElementA", objects.get(0).getName()); @@ -246,12 +237,9 @@ void getIeqQueryUrlMatch() throws QueryParserException { @Test void getIeqQueryUrlMatchMixedCase() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("name:ieq:dAtAeLeMeNta"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("name:ieq:dAtAeLeMeNta")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(1, objects.size()); assertEquals("DataElementA", objects.get(0).getName()); @@ -259,12 +247,9 @@ void getIeqQueryUrlMatchMixedCase() throws QueryParserException { @Test void getIeqQueryUrlNoMatchExtraCharAtStart() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("name:ieq:ddataelementa"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("name:ieq:ddataelementa")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(0, objects.size()); } @@ -285,12 +270,9 @@ void getNeQuery() { @Test void getNeQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("id:ne:deabcdefghA"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("id:ne:deabcdefghA")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(5, objects.size()); assertFalse(collectionContainsUid(objects, "deabcdefghA")); @@ -312,12 +294,9 @@ void getLikeQuery() { @Test void getLikeQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("name:like:F"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("name:like:F")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(1, objects.size()); assertEquals("deabcdefghF", objects.get(0).getUid()); @@ -336,12 +315,9 @@ void getGtQuery() { @Test void getGtQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("created:gt:2003"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("created:gt:2003")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(3, objects.size()); assertTrue(collectionContainsUid(objects, "deabcdefghD")); @@ -361,12 +337,9 @@ void getLtQuery() { @Test void getLtQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("created:lt:2003"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("created:lt:2003")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(2, objects.size()); assertTrue(collectionContainsUid(objects, "deabcdefghA")); @@ -387,12 +360,9 @@ void getGeQuery() { @Test void getGeQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("created:ge:2003"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("created:ge:2003")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(4, objects.size()); assertTrue(collectionContainsUid(objects, "deabcdefghC")); @@ -414,12 +384,9 @@ void getLeQuery() { @Test void getLeQueryUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("created:le:2003"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("created:le:2003")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(3, objects.size()); assertTrue(collectionContainsUid(objects, "deabcdefghA")); @@ -587,12 +554,9 @@ void testIsNotNull() { @Test void testIsNotNullUrl() throws QueryParserException { - Query query = - queryService.getQueryFromUrl( - DataElement.class, - Lists.newArrayList("categoryCombo:!null"), - Lists.newArrayList(), - new Pagination()); + GetObjectListParams params = + new GetObjectListParams().setPaging(false).setFilters(List.of("categoryCombo:!null")); + Query query = queryService.getQueryFromUrl(DataElement.class, params); List objects = queryService.query(query); assertEquals(6, objects.size()); assertTrue(collectionContainsUid(objects, "deabcdefghA")); diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyControllerTest.java index b061b5aed2ee..0621b0724844 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyControllerTest.java @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; /** - * Tests the generic operations offered by the {@link AbstractFullReadOnlyController} using specific + * Tests the generic operations offered by the {@code AbstractFullReadOnlyController} using specific * endpoints. * * @author David Mackessy diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/OrganisationUnitControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/OrganisationUnitControllerTest.java index 81e601a53f36..84bf552d609d 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/OrganisationUnitControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/OrganisationUnitControllerTest.java @@ -190,6 +190,11 @@ void testGetParents() { GET("/organisationUnits/{id}/parents?filter=displayName:ilike:L0", ou21).content(), "L0"); } + @Test + void testGetParents_Root() { + assertListOfOrganisationUnits(GET("/organisationUnits/{id}/parents", ou0).content()); + } + @Test void testGetQuery() { assertListOfOrganisationUnits(GET("/organisationUnits?query=L21").content(), "L21"); diff --git a/dhis-2/dhis-web-api/pom.xml b/dhis-2/dhis-web-api/pom.xml index d2544d095b72..508037ba2f08 100644 --- a/dhis-2/dhis-web-api/pom.xml +++ b/dhis-2/dhis-web-api/pom.xml @@ -291,10 +291,6 @@ org.locationtech.jts jts-core - - org.cache2k - cache2k-api - com.lowagie itext diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java index 6729114fd393..307f00565f57 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractCrudController.java @@ -83,6 +83,7 @@ import org.hisp.dhis.jsonpatch.BulkPatchParameters; import org.hisp.dhis.jsonpatch.JsonPatchManager; import org.hisp.dhis.jsonpatch.validator.BulkPatchValidatorFactory; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.render.RenderService; import org.hisp.dhis.schema.MetadataMergeService; import org.hisp.dhis.schema.validation.SchemaValidator; @@ -117,8 +118,9 @@ @Stable @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(group = OpenApi.Document.GROUP_MANAGE) -public abstract class AbstractCrudController - extends AbstractFullReadOnlyController { +public abstract class AbstractCrudController< + T extends IdentifiableObject, P extends GetObjectListParams> + extends AbstractFullReadOnlyController { @Autowired protected SchemaValidator schemaValidator; @Autowired protected RenderService renderService; @@ -178,9 +180,7 @@ public WebMessage patchObject( IOException, JsonPatchException, ConflictException { - WebOptions options = new WebOptions(rpParameters); - - final T persistedObject = getEntity(pvUid, options); + final T persistedObject = getEntity(pvUid); if (!aclService.canUpdate(currentUser, persistedObject)) { throw new ForbiddenException("You don't have the proper permissions to update this object."); @@ -454,13 +454,10 @@ public WebMessage putJsonObject( @ResponseBody public WebMessage replaceTranslations( @OpenApi.Param(UID.class) @PathVariable("uid") String pvUid, - @RequestParam Map rpParameters, @CurrentUser UserDetails currentUser, HttpServletRequest request) throws NotFoundException, ForbiddenException, IOException { - WebOptions options = new WebOptions(rpParameters); - - BaseIdentifiableObject persistedObject = (BaseIdentifiableObject) getEntity(pvUid, options); + BaseIdentifiableObject persistedObject = (BaseIdentifiableObject) getEntity(pvUid); if (!aclService.canUpdate(currentUser, persistedObject)) { throw new ForbiddenException("You don't have the proper permissions to update this object."); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyController.java index 670851025140..2034dcf490dd 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AbstractFullReadOnlyController.java @@ -27,7 +27,6 @@ */ package org.hisp.dhis.webapi.controller; -import static java.util.stream.Collectors.toList; import static org.springframework.http.CacheControl.noCache; import com.fasterxml.jackson.databind.SequenceWriter; @@ -37,17 +36,16 @@ import com.fasterxml.jackson.dataformat.csv.CsvSchema.Builder; import com.fasterxml.jackson.dataformat.csv.CsvWriteException; import com.google.common.base.Joiner; -import com.google.common.collect.Lists; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.CheckForNull; @@ -64,20 +62,19 @@ import org.hisp.dhis.common.Pager; import org.hisp.dhis.common.PrimaryKeyObject; import org.hisp.dhis.common.UID; -import org.hisp.dhis.dxf2.common.OrderParams; import org.hisp.dhis.feedback.BadRequestException; import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; -import org.hisp.dhis.fieldfilter.Defaults; import org.hisp.dhis.fieldfilter.FieldFilterService; import org.hisp.dhis.fieldfiltering.FieldFilterParams; -import org.hisp.dhis.fieldfiltering.FieldPreset; -import org.hisp.dhis.query.Order; -import org.hisp.dhis.query.Pagination; +import org.hisp.dhis.query.Criterion; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.query.QueryService; +import org.hisp.dhis.query.Restrictions; import org.hisp.dhis.schema.Property; import org.hisp.dhis.schema.PropertyType; import org.hisp.dhis.schema.Schema; @@ -90,10 +87,7 @@ import org.hisp.dhis.webapi.service.ContextService; import org.hisp.dhis.webapi.service.LinkService; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.utils.PaginationUtils; import org.hisp.dhis.webapi.webdomain.StreamingJsonRoot; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -111,11 +105,9 @@ @Maturity.Stable @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(group = OpenApi.Document.GROUP_QUERY) -public abstract class AbstractFullReadOnlyController +public abstract class AbstractFullReadOnlyController< + T extends IdentifiableObject, P extends GetObjectListParams> extends AbstractGistReadOnlyController { - protected static final String DEFAULTS = "INCLUDE"; - - protected static final WebOptions NO_WEB_OPTIONS = new WebOptions(new HashMap<>()); @Autowired protected IdentifiableObjectManager manager; @@ -145,21 +137,22 @@ public abstract class AbstractFullReadOnlyController entityList, WebOptions options, Map parameters) {} + protected void postProcessResponseEntities(List entityList, P params) {} /** * Override to process a single entity after it has been retrieved from storage and before it is * returned to the view. Entity is null-safe. */ - protected void postProcessResponseEntity( - T entity, WebOptions options, Map parameters) {} + protected void postProcessResponseEntity(T entity, GetObjectParams params) {} /** - * Allows to append new filters to the incoming ones. Recommended only on very specific cases - * where forcing a new filter, programmatically, make sense. + * Allows to append further filters to the incoming ones. Recommended only on very specific cases + * where forcing a new filter, programmatically, make sense. This is usually used to ensure that + * some filters are always present. */ - protected void forceFiltering(final WebOptions webOptions, final List filters) {} + protected void addProgrammaticModifiers(P params) {} + + protected void addProgrammaticFilters(Consumer add) {} // -------------------------------------------------------------------------- // GET Full @@ -174,88 +167,93 @@ protected static class ObjectListResponse { List entries; } - @OpenApi.Param(name = "fields", value = String[].class) - @OpenApi.Param(name = "filter", value = String[].class) - @OpenApi.Params(WebOptions.class) @OpenApi.Response(ObjectListResponse.class) @GetMapping public @ResponseBody ResponseEntity> getObjectList( - @RequestParam Map rpParameters, - OrderParams orderParams, + P params, HttpServletResponse response, @CurrentUser UserDetails currentUser) + throws ForbiddenException, BadRequestException, ConflictException { + return getObjectListInternal(params, response, currentUser, getAdditionalFilters(params)); + } + + protected final ResponseEntity> getObjectListWith( + P params, HttpServletResponse response, - @CurrentUser UserDetails currentUser) - throws ForbiddenException, BadRequestException { - return getObjectList( - rpParameters, orderParams, response, currentUser, !rpParameters.containsKey("query"), null); + UserDetails currentUser, + List additionalFilters) + throws ForbiddenException, BadRequestException, ConflictException { + List filters = getAdditionalFilters(params); + filters.addAll(additionalFilters); + return getObjectListInternal(params, response, currentUser, filters); } - protected final ResponseEntity> getObjectList( - @RequestParam Map rpParameters, - OrderParams orderParams, + protected final ResponseEntity> getObjectListInternal( + P params, HttpServletResponse response, - UserDetails userDetails, - boolean countTotal, - @CheckForNull List objects) + UserDetails currentUser, + List additionalFilters) throws ForbiddenException, BadRequestException { - List orders = orderParams.getOrders(getSchema()); - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - - if (fields.isEmpty()) { - fields.addAll(FieldPreset.defaultPreset().getFields()); - } - - WebOptions options = new WebOptions(rpParameters); - WebMetadata metadata = new WebMetadata(); - if (!aclService.canRead(userDetails, getEntityClass())) { + if (!aclService.canRead(currentUser, getEntityClass())) { throw new ForbiddenException( "You don't have the proper permissions to read objects of this type."); } - forceFiltering(options, filters); - - List entities = getEntityList(metadata, options, filters, orders, objects); - - Pager pager = metadata.getPager(); - - if (options.hasPaging() && pager == null) { - long totalCount; + addProgrammaticModifiers(params); - if (!countTotal) { - totalCount = entities.size(); + boolean isAlwaysEmpty = additionalFilters.stream().anyMatch(Criterion::isAlwaysFalse); + List entities = isAlwaysEmpty ? List.of() : getEntityList(params, additionalFilters); + postProcessResponseEntities(entities, params); - long skip = (long) (options.getPage() - 1) * options.getPageSize(); - entities = entities.stream().skip(skip).limit(options.getPageSize()).collect(toList()); - } else { - totalCount = countTotal(options, filters, orders); - } + List fields = params.getFieldsJsonList(); + handleLinksAndAccess(entities, fields, false); - pager = new Pager(options.getPage(), totalCount, options.getPageSize()); + Pager pager = null; + if (params.isPaging()) { + long totalCount = isAlwaysEmpty ? 0 : countGetObjectList(params, additionalFilters); + pager = new Pager(params.getPage(), totalCount, params.getPageSize()); + linkService.generatePagerLinks(pager, getEntityClass()); } - postProcessResponseEntities(entities, options, rpParameters); - - handleLinksAndAccess(entities, fields, false); - linkService.generatePagerLinks(pager, getEntityClass()); - cachePrivate(response); - return ResponseEntity.ok( new StreamingJsonRoot<>( pager, getSchema().getCollectionName(), FieldFilterParams.of(entities, fields), - Defaults.valueOf(options.get("defaults", DEFAULTS)).isExclude())); + params.getDefaults().isExclude())); + } + + /** + * A way to incorporate additional filters to the {@link #getObjectList(GetObjectListParams, + * HttpServletResponse, UserDetails)} endpoint that require running a separate query resulting in + * matching ID list which then is used as filter in the standard query process. + * + * @param params options used + * @return the ID of matches, nor null when no such filter is present/used + */ + @CheckForNull + protected List getPreQueryMatches(P params) throws ConflictException { + return null; // no special filters used + } + + @Nonnull + protected List getAdditionalFilters(P params) throws ConflictException { + List filters = new ArrayList<>(); + if (params.getQuery() != null && !params.getQuery().isEmpty()) + filters.add(Restrictions.query(getSchema(), params.getQuery())); + List matches = getPreQueryMatches(params); + // Note: null = no special filters, empty = no matches for special filters + if (matches != null) filters.add(createIdInFilter(matches)); + return filters; + } + + protected final Criterion createIdInFilter(@Nonnull List matches) { + return Restrictions.in("id", UID.toValueList(matches)); } - @OpenApi.Param(name = "fields", value = String[].class) - @OpenApi.Param(name = "filter", value = String[].class) - @OpenApi.Params(WebOptions.class) @GetMapping(produces = {"text/csv", "application/text"}) public ResponseEntity getObjectListCsv( - @RequestParam Map rpParameters, - OrderParams orderParams, + P params, @CurrentUser UserDetails currentUser, @RequestParam(defaultValue = ",") char separator, @RequestParam(defaultValue = ";") String arraySeparator, @@ -266,16 +264,6 @@ public ResponseEntity getObjectListCsv( ConflictException, ForbiddenException, BadRequestException { - List orders = orderParams.getOrders(getSchema()); - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - - WebOptions options = new WebOptions(rpParameters); - WebMetadata metadata = new WebMetadata(); - - if (fields.isEmpty() || fields.contains("*") || fields.contains(":all")) { - fields.addAll(FieldPreset.defaultPreset().getFields()); - } // only support metadata if (!getSchema().isMetadata()) { @@ -288,8 +276,8 @@ public ResponseEntity getObjectListCsv( "You don't have the proper permissions to read objects of this type."); } - List entities = getEntityList(metadata, options, filters, orders, null); - + List entities = getEntityList(params, List.of()); + List fields = params.getFieldsCsvList(); try { String csv = applyCsvSteps(fields, entities, separator, arraySeparator, skipHeader); return ResponseEntity.ok(csv); @@ -400,15 +388,11 @@ private static Object getAttributeValue(Object obj, String attrId) { return null; } - @OpenApi.Param(name = "fields", value = String[].class) - @OpenApi.Param(name = "filter", value = String[].class) - @OpenApi.Params(WebOptions.class) @OpenApi.Response(OpenApi.EntityType.class) @GetMapping("/{uid:[a-zA-Z0-9]{11}}") - @SuppressWarnings("unchecked") public @ResponseBody ResponseEntity getObject( @OpenApi.Param(UID.class) @PathVariable("uid") String pvUid, - @RequestParam Map rpParameters, + GetObjectParams params, @CurrentUser UserDetails currentUser, HttpServletRequest request, HttpServletResponse response) @@ -419,48 +403,35 @@ private static Object getAttributeValue(Object obj, String attrId) { "You don't have the proper permissions to read objects of this type."); } - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - forceFiltering(new WebOptions(rpParameters), filters); - - if (fields.isEmpty()) { - fields.add("*"); - } - cachePrivate(response); - WebOptions options = new WebOptions(rpParameters); - T entity = getEntity(pvUid, options); + T entity = getEntity(pvUid); - Query query = - queryService.getQueryFromUrl( - getEntityClass(), - filters, - new ArrayList<>(), - getPaginationData(options), - options.getRootJunction()); + GetObjectListParams listParams = params.toListParams(); + addProgrammaticFilters(listParams::addFilter); // temporary workaround + Query query = queryService.getQueryFromUrl(getEntityClass(), listParams); query.setCurrentUserDetails(currentUser); query.setObjects(List.of(entity)); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); + query.setDefaults(params.getDefaults()); + @SuppressWarnings("unchecked") List entities = (List) queryService.query(query); + List fields = params.getFieldsObject(); handleLinksAndAccess(entities, fields, true); - entities.forEach(e -> postProcessResponseEntity(e, options, rpParameters)); + entities.forEach(e -> postProcessResponseEntity(e, params)); return ResponseEntity.ok( new StreamingJsonRoot<>( null, null, FieldFilterParams.of(entities, fields), query.getDefaults().isExclude())); } - @OpenApi.Param(name = "fields", value = String[].class) - @OpenApi.Params(WebOptions.class) @GetMapping("/{uid:[a-zA-Z0-9]{11}}/{property}") public @ResponseBody ResponseEntity getObjectProperty( @OpenApi.Param(UID.class) @PathVariable("uid") String pvUid, @OpenApi.Param(PropertyNames.class) @PathVariable("property") String pvProperty, - @RequestParam Map rpParameters, + @RequestParam(required = false) List fields, @CurrentUser UserDetails currentUser, HttpServletResponse response) throws ForbiddenException, NotFoundException { @@ -470,54 +441,37 @@ private static Object getAttributeValue(Object obj, String attrId) { "You don't have the proper permissions to read objects of this type."); } - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - - if (fields.isEmpty()) { - fields.add(":all"); + if (fields == null || fields.isEmpty()) { + fields = List.of(":all"); } String fieldFilter = "[" + Joiner.on(',').join(fields) + "]"; cachePrivate(response); - ObjectNode objectNode = - getObjectInternal( - pvUid, - rpParameters, - Lists.newArrayList(), - Lists.newArrayList(pvProperty + fieldFilter), - currentUser); + GetObjectParams params = new GetObjectParams(); + params.addField(pvProperty + fieldFilter); + ObjectNode objectNode = getObjectInternal(pvUid, params, currentUser); return ResponseEntity.ok(objectNode); } @SuppressWarnings("unchecked") - private ObjectNode getObjectInternal( - String uid, - Map parameters, - List filters, - List fields, - UserDetails currentUser) + private ObjectNode getObjectInternal(String uid, GetObjectParams params, UserDetails currentUser) throws NotFoundException { - WebOptions options = new WebOptions(parameters); - T entity = getEntity(uid, options); + T entity = getEntity(uid); - Query query = - queryService.getQueryFromUrl( - getEntityClass(), - filters, - new ArrayList<>(), - getPaginationData(options), - options.getRootJunction()); + Query query = queryService.getQueryFromUrl(getEntityClass(), params.toListParams()); query.setCurrentUserDetails(currentUser); query.setObjects(List.of(entity)); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); + query.setDefaults(params.getDefaults()); List entities = (List) queryService.query(query); + List fields = params.getFieldsObject(); handleLinksAndAccess(entities, fields, true); - entities.forEach(e -> postProcessResponseEntity(entity, options, parameters)); + entities.forEach(e -> postProcessResponseEntity(entity, params)); FieldFilterParams filterParams = FieldFilterParams.of(entities, fields); List objectNodes = fieldFilterService.toObjectNodes(filterParams); @@ -525,55 +479,39 @@ private ObjectNode getObjectInternal( return objectNodes.isEmpty() ? fieldFilterService.createObjectNode() : objectNodes.get(0); } - @SuppressWarnings("unchecked") - protected List getEntityList( - WebMetadata metadata, - WebOptions options, - List filters, - List orders, - List objects) + private List getEntityList(P params, List additionalFilters) throws BadRequestException { Query query = BadRequestException.on( QueryParserException.class, - () -> - queryService.getQueryFromUrl( - getEntityClass(), - filters, - orders, - getPaginationData(options), - options.getRootJunction())); + () -> queryService.getQueryFromUrl(getEntityClass(), params)); + query.add(additionalFilters); + query.setDefaultOrder(); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); - query.setObjects(objects); - - // Note: objects being null means no query had been running whereas empty means a query did run - // with no result - if (objects == null && options.getOptions().containsKey("query")) { - return getEntityListPostProcess( - options, - Lists.newArrayList(manager.filter(getEntityClass(), options.getOptions().get("query")))); - } - return getEntityListPostProcess(options, (List) queryService.query(query)); + query.setDefaults(params.getDefaults()); + + modifyGetObjectList(params, query); + + @SuppressWarnings("unchecked") + List res = (List) queryService.query(query); + getEntityListPostProcess(params, res); + return res; } - protected List getEntityListPostProcess(WebOptions options, List entities) { - return entities; + protected void modifyGetObjectList(P params, Query query) { + // by default: nothing special to do } - private long countTotal(WebOptions options, List filters, List orders) + protected void getEntityListPostProcess(P params, List entities) {} + + private long countGetObjectList(P params, List additionalFilters) throws BadRequestException { Query query = BadRequestException.on( QueryParserException.class, - () -> - queryService.getQueryFromUrl( - getEntityClass(), - filters, - orders, - new Pagination(), - options.getRootJunction())); - + () -> queryService.getQueryFromUrl(getEntityClass(), params)); + query.add(additionalFilters); + modifyGetObjectList(params, query); return queryService.count(query); } @@ -607,33 +545,17 @@ private boolean fieldsContains(String match, List fields) { // Reflection helpers // -------------------------------------------------------------------------- - private String entityName; - private String entitySimpleName; - protected final String getEntityName() { - if (entityName == null) { - entityName = getEntityClass().getName(); - } - - return entityName; - } - protected final String getEntitySimpleName() { if (entitySimpleName == null) { entitySimpleName = getEntityClass().getSimpleName(); } - return entitySimpleName; } @Nonnull - protected final T getEntity(String uid) throws NotFoundException { - return getEntity(uid, NO_WEB_OPTIONS); - } - - @Nonnull - protected T getEntity(String uid, WebOptions options) throws NotFoundException { + protected T getEntity(String uid) throws NotFoundException { return getEntity(uid, getEntityClass()) .orElseThrow(() -> new NotFoundException(getEntityClass(), uid)); } @@ -646,12 +568,4 @@ protected final java.util.Optional getEntity( protected final Schema getSchema(Class klass) { return schemaService.getDynamicSchema(klass); } - - // -------------------------------------------------------------------------- - // Helpers - // -------------------------------------------------------------------------- - - protected final Pagination getPaginationData(WebOptions options) { - return PaginationUtils.getPaginationData(options); - } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AggregateDataExchangeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AggregateDataExchangeController.java index 1fa1db292cb9..f8715c580ecb 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AggregateDataExchangeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AggregateDataExchangeController.java @@ -40,6 +40,7 @@ import org.hisp.dhis.dxf2.webmessage.WebMessage; import org.hisp.dhis.dxf2.webmessage.WebMessageUtils; import org.hisp.dhis.feedback.ForbiddenException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.scheduling.JobProgress; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.UserDetails; @@ -63,7 +64,8 @@ @RequiredArgsConstructor @RequestMapping("/api/aggregateDataExchanges") @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) -public class AggregateDataExchangeController extends AbstractCrudController { +public class AggregateDataExchangeController + extends AbstractCrudController { private final AggregateDataExchangeService service; @PostMapping("/exchange") diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AnalyticsTableHookController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AnalyticsTableHookController.java index 590c333adb0f..e9118625c2bc 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AnalyticsTableHookController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AnalyticsTableHookController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.analytics.AnalyticsTableHook; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +39,5 @@ @Controller @RequestMapping("/api/analyticsTableHooks") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class AnalyticsTableHookController extends AbstractCrudController {} +public class AnalyticsTableHookController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AttributeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AttributeController.java index a4f1c97bb99c..7ff36cae4f46 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AttributeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/AttributeController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.attribute.Attribute; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +39,4 @@ @Controller @RequestMapping("/api/attributes") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class AttributeController extends AbstractCrudController {} +public class AttributeController extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ConstantController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ConstantController.java index 1716a6cf8860..a9b998b0de78 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ConstantController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ConstantController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.constant.Constant; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +39,4 @@ @Controller @RequestMapping("/api/constants") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class ConstantController extends AbstractCrudController {} +public class ConstantController extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardController.java index 1ba5f501fb1e..171ce8005c7f 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardController.java @@ -41,6 +41,7 @@ import org.hisp.dhis.dxf2.metadata.MetadataExportParams; import org.hisp.dhis.dxf2.webmessage.WebMessageException; import org.hisp.dhis.feedback.ConflictException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.sharing.CascadeSharingParameters; import org.hisp.dhis.sharing.CascadeSharingReport; import org.hisp.dhis.sharing.CascadeSharingService; @@ -62,7 +63,7 @@ @Controller @RequestMapping("/api/dashboards") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class DashboardController extends AbstractCrudController { +public class DashboardController extends AbstractCrudController { @Autowired private DashboardService dashboardService; @Autowired private CascadeSharingService cascadeSharingService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardItemController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardItemController.java index bcc6b804b5d8..05b3f284d2a9 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardItemController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DashboardItemController.java @@ -38,6 +38,7 @@ import org.hisp.dhis.dashboard.DashboardService; import org.hisp.dhis.dxf2.webmessage.WebMessageException; import org.hisp.dhis.feedback.ForbiddenException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.UserDetails; import org.springframework.beans.factory.annotation.Autowired; @@ -56,7 +57,8 @@ classifiers = {"team:analytics", "purpose:metadata"}) @Controller @RequestMapping("/api/dashboardItems") -public class DashboardItemController extends AbstractCrudController { +public class DashboardItemController + extends AbstractCrudController { // TODO this controller class is only needed for the pre 2.30 old dashboard // app and should be removed diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalLevelController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalLevelController.java index 3d641289e591..6ae3b51202ec 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalLevelController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalLevelController.java @@ -30,6 +30,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataapproval.DataApprovalLevel; import org.hisp.dhis.dataapproval.DataApprovalLevelService; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,7 +38,8 @@ @Controller @RequestMapping("/api/dataApprovalLevels") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataApprovalLevelController extends AbstractCrudController { +public class DataApprovalLevelController + extends AbstractCrudController { @Autowired private DataApprovalLevelService dataApprovalLevelService; @Override diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalWorkflowController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalWorkflowController.java index c4091071c426..aae62bf58db4 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalWorkflowController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalWorkflowController.java @@ -29,10 +29,12 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataapproval.DataApprovalWorkflow; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/api/dataApprovalWorkflows") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataApprovalWorkflowController extends AbstractCrudController {} +public class DataApprovalWorkflowController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataEntryFormController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataEntryFormController.java index c9ccf70ae0be..b26978c3b39f 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataEntryFormController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataEntryFormController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataentryform.DataEntryForm; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +39,5 @@ @Controller @RequestMapping("/api/dataEntryForms") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataEntryFormController extends AbstractCrudController {} +public class DataEntryFormController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataSetController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataSetController.java index ac2fa2a76bdc..c80843aababd 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataSetController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataSetController.java @@ -89,6 +89,7 @@ import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodService; import org.hisp.dhis.period.PeriodType; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.hisp.dhis.webapi.utils.FormUtils; @@ -116,7 +117,7 @@ @Controller @RequestMapping("/api/dataSets") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataSetController extends AbstractCrudController { +public class DataSetController extends AbstractCrudController { public static final String DSD_TRANSFORM = "/templates/metadata2dsd.xsl"; // ------------------------------------------------------------------------- diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DocumentController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DocumentController.java index 5a880b352e3c..6ecf16d9575a 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DocumentController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DocumentController.java @@ -45,6 +45,7 @@ import org.hisp.dhis.external.location.LocationManager; import org.hisp.dhis.fileresource.FileResource; import org.hisp.dhis.fileresource.FileResourceService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.utils.ContextUtils; import org.hisp.dhis.webapi.utils.HeaderUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -62,7 +63,7 @@ @Slf4j @RequestMapping("/api/documents") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DocumentController extends AbstractCrudController { +public class DocumentController extends AbstractCrudController { @Autowired private DocumentService documentService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventFilterController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventFilterController.java index 615ccc4fd921..51f0133cd419 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventFilterController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventFilterController.java @@ -33,6 +33,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.programstagefilter.EventFilter; import org.hisp.dhis.programstagefilter.EventFilterService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -44,7 +45,8 @@ @RequestMapping("/api/eventFilters") @ApiVersion(include = {DhisApiVersion.ALL, DhisApiVersion.DEFAULT}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class EventFilterController extends AbstractCrudController { +public class EventFilterController + extends AbstractCrudController { private final EventFilterService eventFilterService; public EventFilterController(EventFilterService eventFilterService) { diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventHookController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventHookController.java index 76f2cdddae86..b20dd4a239cd 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventHookController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EventHookController.java @@ -31,6 +31,7 @@ import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.eventhook.EventHook; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -43,4 +44,4 @@ @RequestMapping("/api/eventHooks") @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class EventHookController extends AbstractCrudController {} +public class EventHookController extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ExpressionDimensionItemController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ExpressionDimensionItemController.java index 1bbc1e2a4abe..c06a0278fc88 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ExpressionDimensionItemController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ExpressionDimensionItemController.java @@ -31,6 +31,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.expressiondimensionitem.ExpressionDimensionItem; import org.hisp.dhis.feedback.ConflictException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -41,7 +42,7 @@ @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) public class ExpressionDimensionItemController - extends AbstractCrudController { + extends AbstractCrudController { @Override protected void preCreateEntity(ExpressionDimensionItem expressionDimensionItem) throws ConflictException { diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java index 860ce5e735af..c1112a806e2b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java @@ -37,7 +37,6 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; -import java.util.Map; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hisp.dhis.common.DhisApiVersion; @@ -56,6 +55,8 @@ import org.hisp.dhis.fileresource.FileResourceOwner; import org.hisp.dhis.fileresource.FileResourceService; import org.hisp.dhis.fileresource.ImageFileDimension; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.User; import org.hisp.dhis.user.UserDetails; @@ -82,7 +83,8 @@ @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @AllArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class FileResourceController extends AbstractFullReadOnlyController { +public class FileResourceController + extends AbstractFullReadOnlyController { private final FileResourceService fileResourceService; private final FileResourceUtils fileResourceUtils; @@ -95,12 +97,12 @@ public class FileResourceController extends AbstractFullReadOnlyController getObject( @PathVariable String uid, - Map rpParameters, + GetObjectParams params, @CurrentUser UserDetails currentUser, HttpServletRequest request, HttpServletResponse response) throws ForbiddenException, NotFoundException { - return super.getObject(uid, rpParameters, currentUser, request, response); + return super.getObject(uid, params, currentUser, request, response); } @GetMapping(value = "/{uid}") diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/IdentifiableObjectController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/IdentifiableObjectController.java index b6b4bc23c5b6..d40906308d0c 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/IdentifiableObjectController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/IdentifiableObjectController.java @@ -37,9 +37,9 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dxf2.webmessage.WebMessage; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.UserDetails; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.stereotype.Controller; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.PathVariable; @@ -51,12 +51,13 @@ @Controller @RequestMapping("/api/identifiableObjects") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class IdentifiableObjectController extends AbstractCrudController { +public class IdentifiableObjectController + extends AbstractCrudController { @Nonnull @Override @SuppressWarnings("unchecked") - public IdentifiableObject getEntity(String uid, WebOptions options) throws NotFoundException { + public IdentifiableObject getEntity(String uid) throws NotFoundException { Optional object = (Optional) manager.find(uid); if (object.isEmpty()) { throw new NotFoundException(format("No identifiable object with id `%s` exists", uid)); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/InterpretationController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/InterpretationController.java index 2cbe281f86ed..dd8a9fb7c87e 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/InterpretationController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/InterpretationController.java @@ -31,13 +31,9 @@ import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.created; import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.notFound; -import com.google.common.collect.Lists; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; -import java.util.List; import org.hisp.dhis.common.AnalyticalObject; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.OpenApi; @@ -48,29 +44,20 @@ import org.hisp.dhis.eventreport.EventReport; import org.hisp.dhis.eventvisualization.EventVisualization; import org.hisp.dhis.feedback.ForbiddenException; -import org.hisp.dhis.fieldfilter.Defaults; import org.hisp.dhis.interpretation.Interpretation; import org.hisp.dhis.interpretation.InterpretationComment; import org.hisp.dhis.interpretation.InterpretationService; -import org.hisp.dhis.interpretation.MentionUtils; import org.hisp.dhis.mapping.Map; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodType; -import org.hisp.dhis.query.Disjunction; -import org.hisp.dhis.query.Order; -import org.hisp.dhis.query.Query; -import org.hisp.dhis.query.QueryParserException; -import org.hisp.dhis.query.Restrictions; -import org.hisp.dhis.schema.Schema; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.schema.descriptors.InterpretationSchemaDescriptor; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; import org.hisp.dhis.user.UserDetails; import org.hisp.dhis.visualization.Visualization; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; @@ -90,51 +77,12 @@ @Controller @RequestMapping("/api/interpretations") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class InterpretationController extends AbstractCrudController { +public class InterpretationController + extends AbstractCrudController { @Autowired private InterpretationService interpretationService; @Autowired private IdentifiableObjectManager idObjectManager; - @Override - @SuppressWarnings("unchecked") - protected List getEntityList( - WebMetadata metadata, - WebOptions options, - List filters, - List orders, - List objects) - throws QueryParserException { - // If custom filter (mentions:in:[username]) in filters -> Remove from - // filters - List mentionsFromCustomFilters = MentionUtils.removeCustomFilters(filters); - - Query query = - queryService.getQueryFromUrl( - getEntityClass(), - filters, - orders, - getPaginationData(options), - options.getRootJunction()); - query.setDefaultOrder(); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); - // If custom filter (mentions:in:[username]) in filters -> Add as - // disjunction including interpretation mentions and comments mentions - for (Disjunction disjunction : - (Collection) - getDisjunctionsFromCustomMentions(mentionsFromCustomFilters, query.getSchema())) { - query.add(disjunction); - } - - List entityList; - if (objects == null && options.getOptions().containsKey("query")) { - entityList = - Lists.newArrayList(manager.filter(getEntityClass(), options.getOptions().get("query"))); - } else { - entityList = (List) queryService.query(query); - } - return entityList; - } - // ------------------------------------------------------------------------- // Interpretation create // ------------------------------------------------------------------------- @@ -500,22 +448,4 @@ public WebMessage unlike(@PathVariable("uid") String uid) { } return conflict("Could not remove like, user had not previously liked interpretation"); } - - // ------------------------------------------------------------------------- - // Supportive methods - // ------------------------------------------------------------------------- - - private Collection getDisjunctionsFromCustomMentions( - List mentions, Schema schema) { - Collection disjunctions = new ArrayList(); - for (String m : mentions) { - Disjunction disjunction = new Disjunction(schema); - String[] split = m.substring(1, m.length() - 1).split(","); - List items = Lists.newArrayList(split); - disjunction.add(Restrictions.in("mentions.username", items)); - disjunction.add(Restrictions.in("comments.mentions.username", items)); - disjunctions.add(disjunction); - } - return disjunctions; - } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/LegendSetController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/LegendSetController.java index 2bbe7453ffe9..a24d2f6c9ccf 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/LegendSetController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/LegendSetController.java @@ -40,6 +40,7 @@ import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.legend.LegendSet; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.user.CurrentUser; import org.hisp.dhis.user.UserDetails; @@ -54,7 +55,7 @@ @Controller @RequestMapping("/api/legendSets") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class LegendSetController extends AbstractCrudController { +public class LegendSetController extends AbstractCrudController { @Override @RequiresAuthority(anyOf = {F_LEGEND_SET_PUBLIC_ADD, F_LEGEND_SET_PRIVATE_ADD}) public WebMessage postJsonObject(HttpServletRequest request) diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/MessageConversationController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/MessageConversationController.java index 66e78789eace..e4a5ad88d55f 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/MessageConversationController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/MessageConversationController.java @@ -31,6 +31,7 @@ import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.created; import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.notFound; import static org.hisp.dhis.security.Authorities.F_METADATA_IMPORT; +import static org.hisp.dhis.user.CurrentUserUtil.getCurrentUserDetails; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; @@ -38,18 +39,16 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; +import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import org.hisp.dhis.common.OpenApi; -import org.hisp.dhis.common.Pager; +import org.hisp.dhis.common.UID; import org.hisp.dhis.configuration.ConfigurationService; import org.hisp.dhis.dxf2.webmessage.WebMessage; import org.hisp.dhis.dxf2.webmessage.WebMessageException; @@ -57,7 +56,6 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; -import org.hisp.dhis.fieldfilter.Defaults; import org.hisp.dhis.fileresource.FileResource; import org.hisp.dhis.fileresource.FileResourceDomain; import org.hisp.dhis.fileresource.FileResourceService; @@ -72,15 +70,13 @@ import org.hisp.dhis.node.types.SimpleNode; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.organisationunit.OrganisationUnitService; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.query.Junction; -import org.hisp.dhis.query.Order; -import org.hisp.dhis.query.Pagination; import org.hisp.dhis.query.Query; -import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.schema.descriptors.MessageConversationSchemaDescriptor; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.user.CurrentUser; -import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; import org.hisp.dhis.user.UserDetails; import org.hisp.dhis.user.UserGroup; @@ -88,8 +84,6 @@ import org.hisp.dhis.webapi.utils.ContextUtils; import org.hisp.dhis.webapi.utils.FileResourceUtils; import org.hisp.dhis.webapi.webdomain.MessageConversation; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -113,7 +107,10 @@ @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:support"}) public class MessageConversationController - extends AbstractCrudController { + extends AbstractCrudController< + org.hisp.dhis.message.MessageConversation, + MessageConversationController.GetMessageConversationObjectListParams> { + private final MessageService messageService; private final OrganisationUnitService organisationUnitService; private final UserGroupService userGroupService; @@ -122,30 +119,29 @@ public class MessageConversationController private final FileResourceService fileResourceService; private final DhisConfigurationProvider dhisConfig; + @Data + @EqualsAndHashCode(callSuper = true) + public static final class GetMessageConversationObjectListParams extends GetObjectListParams { + String queryString; + String queryOperator; + } + @Override protected void postProcessResponseEntity( - org.hisp.dhis.message.MessageConversation entity, - WebOptions options, - Map parameters) { + org.hisp.dhis.message.MessageConversation entity, GetObjectParams params) { - User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); - if (!messageService.hasAccessToManageFeedbackMessages(UserDetails.fromUser(currentUser))) { + if (!messageService.hasAccessToManageFeedbackMessages(getCurrentUserDetails())) { entity.setMessages( entity.getMessages().stream().filter(message -> !message.isInternal()).toList()); } - - boolean markRead = Boolean.parseBoolean(parameters.get("markRead")); - - if (markRead) { - entity.markRead(currentUser); - manager.update(entity); - } } @Override + @OpenApi.Param(name = "markRead", value = boolean.class) + @GetMapping("/{uid:[a-zA-Z0-9]{11}}") public ResponseEntity getObject( @PathVariable String uid, - Map rpParameters, + GetObjectParams params, @CurrentUser UserDetails currentUser, HttpServletRequest request, HttpServletResponse response) @@ -167,80 +163,40 @@ public ResponseEntity getObject( throw new ForbiddenException("Not authorized to access this conversation."); } - return super.getObject(uid, rpParameters, currentUser, request, response); - } - - @Override - @SuppressWarnings("unchecked") - protected List getEntityList( - WebMetadata metadata, - WebOptions options, - List filters, - List orders, - List objects) - throws QueryParserException { - List messageConversations; - - if (objects == null && options.getOptions().containsKey("query")) { - messageConversations = - Lists.newArrayList(manager.filter(getEntityClass(), options.getOptions().get("query"))); - } else { - messageConversations = new ArrayList<>(messageService.getMessageConversations()); - } - - Query query = - queryService.getQueryFromUrl( - getEntityClass(), filters, orders, new Pagination(), options.getRootJunction()); - query.setDefaultOrder(); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); - query.setObjects(messageConversations); - - messageConversations = - (List) queryService.query(query); - - if (options.get("queryString") != null) { - String queryOperator = "token"; - if (options.get("queryOperator") != null) { - queryOperator = options.get("queryOperator"); - } + boolean markRead = "true".equals(request.getParameter("markRead")); - List queryFilter = - Arrays.asList( - "subject:" + queryOperator + ":" + options.get("queryString"), - "messages.text:" + queryOperator + ":" + options.get("queryString"), - "messages.sender.displayName:" + queryOperator + ":" + options.get("queryString")); - Query subQuery = - queryService.getQueryFromUrl( - getEntityClass(), - queryFilter, - Collections.emptyList(), - new Pagination(), - Junction.Type.OR); - subQuery.setObjects(messageConversations); - messageConversations = - (List) queryService.query(subQuery); + if (markRead) { + messageConversation.markRead(UID.of(currentUser.getUid())); + manager.update(messageConversation); } - int count = messageConversations.size(); - - Query paginatedQuery = - queryService.getQueryFromUrl( - getEntityClass(), - Collections.emptyList(), - Collections.emptyList(), - getPaginationData(options), - options.getRootJunction()); - paginatedQuery.setObjects(messageConversations); - - messageConversations = - (List) queryService.query(paginatedQuery); - - if (options.hasPaging()) { - Pager pager = new Pager(options.getPage(), count, options.getPageSize()); - metadata.setPager(pager); - } + return super.getObject(uid, params, currentUser, request, response); + } - return messageConversations; + @Override + protected List getPreQueryMatches(GetMessageConversationObjectListParams params) { + String query = params.getQueryString(); + if (query == null) return null; + + String op = params.getQueryOperator(); + if (op == null) op = "token"; + + List filters = + List.of( + "subject:" + op + ":" + query, + "messages.text:" + op + ":" + query, + "messages.sender.displayName:" + op + ":" + query); + + GetObjectListParams subQueryParams = + new GetObjectListParams() + .setPaging(false) + .setRootJunction(Junction.Type.OR) + .setFilters(filters); + Query subQuery = queryService.getQueryFromUrl(getEntityClass(), subQueryParams); + // Note: in theory these filters could be added to the main query + // but the OR concerns both DB and in-memory properties + // which would break if added to the main query ATM + return queryService.query(subQuery).stream().map(UID::of).toList(); } // -------------------------------------------------------------------------- @@ -1061,7 +1017,10 @@ private RootNode modifyMessageConversationRead( for (org.hisp.dhis.message.MessageConversation conversation : messageConversations) { - boolean success = (readValue ? conversation.markRead(user) : conversation.markUnread(user)); + boolean success = + (readValue + ? conversation.markRead(UID.of(user.getUid())) + : conversation.markUnread(user)); if (success) { messageService.updateMessageConversation(conversation); marked.addChild(new SimpleNode("uid", conversation.getUid())); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorController.java index 82b58f462564..095692195e9a 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorController.java @@ -47,6 +47,7 @@ import org.hisp.dhis.predictor.PredictionSummary; import org.hisp.dhis.predictor.Predictor; import org.hisp.dhis.predictor.PredictorService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -66,7 +67,7 @@ @Slf4j @RequestMapping("/api/predictors") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class PredictorController extends AbstractCrudController { +public class PredictorController extends AbstractCrudController { @Autowired private PredictorService predictorService; @Autowired private PredictionService predictionService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorGroupController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorGroupController.java index 2a3a7e1cd23d..10d2739b2a3d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorGroupController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PredictorGroupController.java @@ -39,6 +39,7 @@ import org.hisp.dhis.predictor.PredictionService; import org.hisp.dhis.predictor.PredictionSummary; import org.hisp.dhis.predictor.PredictorGroup; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.scheduling.JobProgress; import org.hisp.dhis.security.RequiresAuthority; import org.springframework.stereotype.Controller; @@ -55,7 +56,8 @@ @RequestMapping("/api/predictorGroups") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class PredictorGroupController extends AbstractCrudController { +public class PredictorGroupController + extends AbstractCrudController { private final PredictionService predictionService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ProgramStageWorkingListController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ProgramStageWorkingListController.java index 1128a591dbd6..6734fdfba2ee 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ProgramStageWorkingListController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ProgramStageWorkingListController.java @@ -30,6 +30,7 @@ import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.programstageworkinglist.ProgramStageWorkingList; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,4 @@ @ApiVersion(include = {DhisApiVersion.ALL, DhisApiVersion.DEFAULT}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) public class ProgramStageWorkingListController - extends AbstractCrudController {} + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PushAnalysisController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PushAnalysisController.java index 180ef58cc42f..725f4a462f4b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PushAnalysisController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/PushAnalysisController.java @@ -41,6 +41,7 @@ import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.pushanalysis.PushAnalysis; import org.hisp.dhis.pushanalysis.PushAnalysisService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.scheduling.JobConfiguration; import org.hisp.dhis.scheduling.JobConfigurationService; import org.hisp.dhis.scheduling.JobSchedulerService; @@ -67,7 +68,8 @@ @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:support"}) -public class PushAnalysisController extends AbstractCrudController { +public class PushAnalysisController + extends AbstractCrudController { private final PushAnalysisService pushAnalysisService; private final ContextUtils contextUtils; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ReportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ReportController.java index 96a8afe769ed..b44644735a43 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ReportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/ReportController.java @@ -46,6 +46,7 @@ import org.hisp.dhis.period.MonthlyPeriodType; import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodType; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.report.Report; import org.hisp.dhis.report.ReportService; import org.hisp.dhis.report.ReportType; @@ -72,7 +73,7 @@ @Controller @RequestMapping("/api/reports") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class ReportController extends AbstractCrudController { +public class ReportController extends AbstractCrudController { @Autowired public ReportService reportService; @Autowired private OrganisationUnitService organisationUnitService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/RouteController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/RouteController.java index a02412bf474d..fdda5c0d2d13 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/RouteController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/RouteController.java @@ -39,6 +39,7 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.route.Route; import org.hisp.dhis.route.RouteService; import org.hisp.dhis.schema.descriptors.RouteSchemaDescriptor; @@ -62,7 +63,7 @@ @RequestMapping("/api/routes") @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:extensibility", "purpose:metadata"}) -public class RouteController extends AbstractCrudController { +public class RouteController extends AbstractCrudController { private final RouteService routeService; @RequestMapping( diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SectionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SectionController.java index 211f5a6b8b96..2877e3058f3b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SectionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SectionController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataset.Section; +import org.hisp.dhis.query.GetObjectListParams; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +39,4 @@ @Controller @RequestMapping("/api/sections") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class SectionController extends AbstractCrudController
{} +public class SectionController extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SqlViewController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SqlViewController.java index 119fe8053fe2..418c48c1e1d9 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SqlViewController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SqlViewController.java @@ -46,6 +46,7 @@ import org.hisp.dhis.external.conf.DhisConfigurationProvider; import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.scheduling.JobConfiguration; import org.hisp.dhis.scheduling.JobConfigurationService; import org.hisp.dhis.scheduling.JobType; @@ -73,7 +74,7 @@ @RequestMapping("/api/sqlViews") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:support"}) -public class SqlViewController extends AbstractCrudController { +public class SqlViewController extends AbstractCrudController { private final SqlViewService sqlViewService; private final JobConfigurationService jobConfigurationService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/TrackedEntityFilterController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/TrackedEntityFilterController.java index 9d26fbeb5184..b8760b71e09c 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/TrackedEntityFilterController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/TrackedEntityFilterController.java @@ -31,6 +31,7 @@ import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.IllegalQueryException; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.trackedentityfilter.TrackedEntityFilter; import org.hisp.dhis.trackedentityfilter.TrackedEntityFilterService; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; @@ -44,7 +45,8 @@ @RequestMapping("/api/trackedEntityInstanceFilters") @ApiVersion(include = {DhisApiVersion.ALL, DhisApiVersion.DEFAULT}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class TrackedEntityFilterController extends AbstractCrudController { +public class TrackedEntityFilterController + extends AbstractCrudController { private final TrackedEntityFilterService teiFilterService; public TrackedEntityFilterController(TrackedEntityFilterService teiFilterService) { diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/VisualizationController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/VisualizationController.java index a163845380a3..18592c1d6fe7 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/VisualizationController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/VisualizationController.java @@ -37,7 +37,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.hisp.dhis.common.BaseDimensionalItemObject; @@ -53,17 +52,19 @@ import org.hisp.dhis.legend.LegendSetService; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; import org.hisp.dhis.visualization.Visualization; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/api/visualizations") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class VisualizationController extends AbstractCrudController { +public class VisualizationController + extends AbstractCrudController { private final LegendSetService legendSetService; private final DimensionService dimensionService; @@ -141,7 +142,7 @@ private void maybeLoadLegendSetInto(Visualization visualization) { @Override public void postProcessResponseEntities( - List entityList, WebOptions options, Map parameters) { + List entityList, GetObjectListParams params) { if (CollectionUtils.isEmpty(entityList)) return; User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); @@ -169,8 +170,7 @@ public void postProcessResponseEntities( } @Override - public void postProcessResponseEntity( - Visualization visualization, WebOptions options, Map parameters) { + public void postProcessResponseEntity(Visualization visualization, GetObjectParams params) { if (visualization != null) { visualization.populateAnalyticalProperties(); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryComboController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryComboController.java index 8d811020490d..d10ebbd19881 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryComboController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryComboController.java @@ -38,6 +38,7 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -53,7 +54,8 @@ @Controller @RequestMapping("/api/categoryCombos") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class CategoryComboController extends AbstractCrudController { +public class CategoryComboController + extends AbstractCrudController { @Autowired private CategoryService categoryService; @Autowired private DataValueService dataValueService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryController.java index d1cfec59d1ea..7fb6dc1595bd 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.category.Category; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,4 @@ @Controller @RequestMapping("/api/categories") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class CategoryController extends AbstractCrudController {} +public class CategoryController extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionComboController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionComboController.java index ddfab84e9a08..e243d3a836a0 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionComboController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionComboController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.category.CategoryOptionCombo; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/categoryOptionCombos") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class CategoryOptionComboController extends AbstractCrudController {} +public class CategoryOptionComboController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java index cbe2b8fa9b36..f828daa7bc37 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionController.java @@ -50,6 +50,7 @@ import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -70,7 +71,8 @@ @RequestMapping("/api/categoryOptions") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class CategoryOptionController extends AbstractCrudController { +public class CategoryOptionController + extends AbstractCrudController { private final CategoryService categoryService; private final MergeService categoryOptionMergeService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupController.java index f1b548320189..e96f98af42f8 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.category.CategoryOptionGroup; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/categoryOptionGroups") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class CategoryOptionGroupController extends AbstractCrudController {} +public class CategoryOptionGroupController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupSetController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupSetController.java index 9c6ff8e3a40b..6ca10099199c 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupSetController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/category/CategoryOptionGroupSetController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.category.CategoryOptionGroupSet; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -40,4 +41,4 @@ @RequestMapping("/api/categoryOptionGroupSets") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) public class CategoryOptionGroupSetController - extends AbstractCrudController {} + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java index bc6b4422970d..4e161536dc8e 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementController.java @@ -43,6 +43,7 @@ import org.hisp.dhis.feedback.MergeReport; import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -61,7 +62,8 @@ @RequestMapping("/api/dataElements") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataElementController extends AbstractCrudController { +public class DataElementController + extends AbstractCrudController { private final DataElementService dataElementService; private final MergeService dataElementMergeService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupController.java index 0be59f38e91d..0914e655202d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupController.java @@ -45,6 +45,7 @@ import org.hisp.dhis.dxf2.metadata.MetadataExportParams; import org.hisp.dhis.dxf2.webmessage.WebMessageException; import org.hisp.dhis.feedback.NotFoundException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.webdomain.WebMetadata; import org.hisp.dhis.webapi.webdomain.WebOptions; @@ -63,7 +64,8 @@ @Controller @RequestMapping("/api/dataElementGroups") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataElementGroupController extends AbstractCrudController { +public class DataElementGroupController + extends AbstractCrudController { @Autowired private CategoryService dataElementCategoryService; @Autowired private DataElementService dataElementService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupSetController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupSetController.java index a337845383b4..467671d2898d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupSetController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementGroupSetController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataelement.DataElementGroupSet; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/dataElementGroupSets") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class DataElementGroupSetController extends AbstractCrudController {} +public class DataElementGroupSetController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementOperandController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementOperandController.java index 6d6f049637cc..61a5b860dbef 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementOperandController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dataelement/DataElementOperandController.java @@ -32,50 +32,39 @@ import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; +import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; -import org.cache2k.Cache; -import org.cache2k.Cache2kBuilder; import org.hisp.dhis.category.CategoryService; import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.IdentifiableObjectManager; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.common.Pager; +import org.hisp.dhis.common.UID; import org.hisp.dhis.common.collection.CollectionUtils; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementGroup; import org.hisp.dhis.dataelement.DataElementOperand; import org.hisp.dhis.dataset.DataSet; -import org.hisp.dhis.dxf2.common.OrderParams; import org.hisp.dhis.fieldfilter.FieldFilterParams; import org.hisp.dhis.fieldfilter.FieldFilterService; -import org.hisp.dhis.fieldfiltering.FieldPreset; import org.hisp.dhis.node.NodeUtils; import org.hisp.dhis.node.types.RootNode; -import org.hisp.dhis.query.Order; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.query.QueryService; -import org.hisp.dhis.schema.Schema; -import org.hisp.dhis.schema.SchemaService; -import org.hisp.dhis.user.CurrentUser; -import org.hisp.dhis.user.User; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; -import org.hisp.dhis.webapi.service.ContextService; import org.hisp.dhis.webapi.service.LinkService; -import org.hisp.dhis.webapi.utils.PaginationUtils; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** * @author Morten Olav Hansen */ +@OpenApi.EntityType(DataElementOperand.class) @OpenApi.Document( entity = DataElementOperand.class, classifiers = {"team:platform", "purpose:metadata"}) @@ -92,45 +81,33 @@ public class DataElementOperandController { private final LinkService linkService; - private final ContextService contextService; - - private final SchemaService schemaService; - private final CategoryService dataElementCategoryService; - private final Cache paginationCountCache = - new Cache2kBuilder() {}.expireAfterWrite(1, TimeUnit.MINUTES).build(); - - @GetMapping - @SuppressWarnings("unchecked") - public @ResponseBody RootNode getObjectList( - @RequestParam Map rpParameters, - OrderParams orderParams, - @CurrentUser User currentUser) - throws QueryParserException { - Schema schema = schemaService.getDynamicSchema(DataElementOperand.class); - - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - List orders = orderParams.getOrders(schema); + @Data + @EqualsAndHashCode(callSuper = true) + public static final class GetDataElementOperandObjectListParams extends GetObjectListParams { + boolean persisted; + boolean totals; - if (fields.isEmpty()) { - fields.addAll(FieldPreset.ALL.getFields()); - } + @OpenApi.Property({UID.class, DataSet.class}) + String dataSet; + } - WebOptions options = new WebOptions(rpParameters); - WebMetadata metadata = new WebMetadata(); + private List getUnfilteredDataElementOperands( + GetDataElementOperandObjectListParams params) { List dataElementOperands = List.of(); - if (options.isTrue("persisted")) { + if (params.isPersisted()) { dataElementOperands = Lists.newArrayList(manager.getAll(DataElementOperand.class)); } else { - boolean totals = options.isTrue("totals"); + boolean totals = params.isTotals(); - String deg = CollectionUtils.popStartsWith(filters, "dataElement.dataElementGroups.id:eq:"); + String deg = + CollectionUtils.popStartsWith( + params.getFilters(), "dataElement.dataElementGroups.id:eq:"); deg = deg != null ? deg.substring("dataElement.dataElementGroups.id:eq:".length()) : null; - String ds = options.get("dataSet"); + String ds = params.getDataSet(); if (deg != null) { DataElementGroup dataElementGroup = manager.get(DataElementGroup.class, deg); @@ -146,71 +123,52 @@ public class DataElementOperandController { dataElementOperands = dataElementCategoryService.getOperands(dataElements, totals); } } + return dataElementOperands; + } + + @GetMapping + @SuppressWarnings("unchecked") + public @ResponseBody RootNode getObjectList(GetDataElementOperandObjectListParams params) + throws QueryParserException { + List allItems = getUnfilteredDataElementOperands(params); // This is needed for two reasons: // 1) We are doing in-memory paging; // 2) We have to count all items respecting the filtering and the // initial universe of elements. In this case, the variable // "dataElementOperands". - Query queryForCount = queryService.getQueryFromUrl(DataElementOperand.class, filters, orders); - queryForCount.setObjects(dataElementOperands); - - List totalOfItems = - (List) queryService.query(queryForCount); - - Query query = - queryService.getQueryFromUrl( - DataElementOperand.class, - filters, - orders, - PaginationUtils.getPaginationData(options), - options.getRootJunction()); - query.setDefaultOrder(); - query.setObjects(dataElementOperands); - - dataElementOperands = (List) queryService.query(query); - Pager pager = metadata.getPager(); - - if (options.hasPaging() && pager == null) { - final long countTotal = isNotEmpty(totalOfItems) ? totalOfItems.size() : 0; - - // fetch the count for the current query from a short-lived cache - - long cachedCountTotal = - !isFilterNullOrBlank(options.get("filter")) - ? paginationCountCache.computeIfAbsent( - calculatePaginationCountKey(currentUser, options), () -> countTotal) - : countTotal; - - pager = new Pager(options.getPage(), cachedCountTotal, options.getPageSize()); - + Pager pager = null; + if (params.isPaging()) { + params.setPaging(false); + Query queryForCount = queryService.getQueryFromUrl(DataElementOperand.class, params); + queryForCount.setObjects(allItems); + + List totalOfItems = queryService.query(queryForCount); + int countTotal = isNotEmpty(totalOfItems) ? totalOfItems.size() : 0; + pager = new Pager(params.getPage(), countTotal, params.getPageSize()); linkService.generatePagerLinks(pager, DataElementOperand.class); + params.setPaging(true); } + Query query = queryService.getQueryFromUrl(DataElementOperand.class, params); + query.setDefaultOrder(); + query.setObjects(allItems); + + List pageItems = (List) queryService.query(query); + RootNode rootNode = NodeUtils.createMetadata(); if (pager != null) { rootNode.addChild(NodeUtils.createPager(pager)); } + List fields = params.getFields(); + if (fields == null || fields.isEmpty()) fields = List.of("*"); + rootNode.addChild( fieldFilterService.toCollectionNode( - DataElementOperand.class, new FieldFilterParams(dataElementOperands, fields))); + DataElementOperand.class, new FieldFilterParams(pageItems, fields))); return rootNode; } - - private String calculatePaginationCountKey(User currentUser, WebOptions options) { - return currentUser.getUsername() - + "." - + "DataElementOperand" - + "." - + options.get("filter") - + "." - + options.getRootJunction().name(); - } - - private boolean isFilterNullOrBlank(String filter) { - return filter == null || filter.isBlank(); - } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionController.java index 55a792e96f9b..a90fc377956d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionController.java @@ -27,19 +27,16 @@ */ package org.hisp.dhis.webapi.controller.dimension; -import static com.google.common.collect.Lists.newArrayList; import static java.lang.String.format; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hisp.dhis.common.CodeGenerator.isValidUid; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.Lists; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import lombok.RequiredArgsConstructor; @@ -55,18 +52,16 @@ import org.hisp.dhis.common.Pager; import org.hisp.dhis.commons.jackson.domain.JsonRoot; import org.hisp.dhis.dataset.DataSet; -import org.hisp.dhis.dxf2.common.OrderParams; import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.fieldfilter.FieldFilterParams; import org.hisp.dhis.fieldfiltering.FieldPath; -import org.hisp.dhis.fieldfiltering.FieldPreset; import org.hisp.dhis.hibernate.InternalHibernateGenericStore; import org.hisp.dhis.node.AbstractNode; import org.hisp.dhis.node.Node; import org.hisp.dhis.node.NodeUtils; import org.hisp.dhis.node.types.CollectionNode; import org.hisp.dhis.node.types.RootNode; -import org.hisp.dhis.query.Order; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.user.CurrentUser; @@ -77,8 +72,6 @@ import org.hisp.dhis.webapi.utils.PaginationUtils.PagedEntities; import org.hisp.dhis.webapi.webdomain.StreamingJsonRoot; import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; -import org.hisp.dhis.webapi.webdomain.WebRequestData; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -95,7 +88,8 @@ @Controller @RequestMapping("/api/dimensions") @RequiredArgsConstructor -public class DimensionController extends AbstractCrudController { +public class DimensionController + extends AbstractCrudController { // ------------------------------------------------------------------------- // Dependencies @@ -116,7 +110,7 @@ public class DimensionController extends AbstractCrudController> getObjectList( - @RequestParam Map rpParameters, - OrderParams orderParams, + GetObjectListParams params, HttpServletResponse response, @CurrentUser UserDetails currentUser) { - WebRequestData requestData = applyRequestSetup(rpParameters, orderParams); - PagedEntities pagedEntities = getPagedEntities(requestData); + addProgrammaticModifiers(params); + PagedEntities pagedEntities = getPagedEntities(params); linkService.generatePagerLinks(pagedEntities.pager(), RESOURCE_PATH); return ResponseEntity.ok( @@ -152,14 +140,13 @@ protected DimensionalObject getEntity(String uid, WebOptions options) throws Not pagedEntities.pager(), getSchema().getCollectionName(), org.hisp.dhis.fieldfiltering.FieldFilterParams.of( - pagedEntities.entities(), requestData.fields()))); + pagedEntities.entities(), params.getFieldsJsonList()))); } @Override @GetMapping(produces = {"text/csv", "application/text"}) public ResponseEntity getObjectListCsv( - @RequestParam Map rpParameters, - OrderParams orderParams, + GetObjectListParams params, @CurrentUser UserDetails currentUser, @RequestParam(defaultValue = ",") char separator, @RequestParam(defaultValue = ";") String arraySeparator, @@ -167,11 +154,15 @@ public ResponseEntity getObjectListCsv( HttpServletResponse response) throws IOException { - WebRequestData requestData = applyRequestSetup(rpParameters, orderParams); - PagedEntities pagedEntities = getPagedEntities(requestData); + addProgrammaticModifiers(params); + PagedEntities pagedEntities = getPagedEntities(params); String csv = applyCsvSteps( - requestData.fields(), pagedEntities.entities(), separator, arraySeparator, skipHeader); + params.getFieldsCsvList(), + pagedEntities.entities(), + separator, + arraySeparator, + skipHeader); return ResponseEntity.ok(csv); } @@ -182,47 +173,42 @@ public ResponseEntity getObjectListCsv( @OpenApi.Property(name = "pager", value = Pager.class), @OpenApi.Property(name = "items", value = BaseDimensionalItemObject[].class) }) - @SuppressWarnings("unchecked") @GetMapping("/{uid}/items") - public @ResponseBody RootNode getItems( - @PathVariable String uid, - @RequestParam Map parameters, - OrderParams orderParams) + public @ResponseBody RootNode getItems(@PathVariable String uid, GetObjectListParams params) throws QueryParserException { - List fields = newArrayList(contextService.getParameterValues("fields")); - List filters = newArrayList(contextService.getParameterValues("filter")); - List orders = orderParams.getOrders(getSchema(DimensionalItemObject.class)); - WebOptions options = new WebOptions(parameters); - - if (fields.isEmpty()) { - fields.addAll(FieldPreset.defaultPreset().getFields()); - } // This is the base list used in this flow. It contains only items // allowed to the current user. List readableItems = dimensionService.getCanReadDimensionItems(uid); + // The query engine is just used as a tool to do in-memory filtering // This is needed for two reasons: // 1) We are doing in-memory paging; // 2) We have to count all items respecting the filtering. - Query queryForCount = - queryService.getQueryFromUrl(DimensionalItemObject.class, filters, orders); - queryForCount.setObjects(readableItems); - - List forCountItems = - (List) queryService.query(queryForCount); + boolean paging = params.isPaging(); + int totalOfItems = 0; + if (paging) { + params.setPaging(false); + Query queryForCount = queryService.getQueryFromUrl(DimensionalItemObject.class, params); + queryForCount.setObjects(readableItems); + + List totalItems = queryService.query(queryForCount); + totalOfItems = isNotEmpty(totalItems) ? totalItems.size() : 0; + params.setPaging(true); + } - Query query = - queryService.getQueryFromUrl( - DimensionalItemObject.class, filters, orders, getPaginationData(options)); + Query query = queryService.getQueryFromUrl(DimensionalItemObject.class, params); query.setObjects(readableItems); query.setDefaultOrder(); + @SuppressWarnings("unchecked") List paginatedItems = (List) queryService.query(query); + if (!paging) totalOfItems = paginatedItems.size(); RootNode rootNode = NodeUtils.createMetadata(); + List fields = params.getFieldsJsonList(); CollectionNode collectionNode = rootNode.addChild( oldFieldFilterService.toCollectionNode( @@ -234,8 +220,7 @@ public ResponseEntity getObjectListCsv( } // Adding pagination elements to the root node. - final int totalOfItems = isNotEmpty(forCountItems) ? forCountItems.size() : 0; - dimensionItemPageHandler.addPaginationToNodeIfEnabled(rootNode, options, uid, totalOfItems); + dimensionItemPageHandler.addPaginationToNodeIfEnabled(rootNode, params, uid, totalOfItems); return rootNode; } @@ -324,43 +309,14 @@ public ResponseEntity getDimensionsForDataSet( return ResponseEntity.ok(new JsonRoot("dimensions", objectNodes)); } - private PagedEntities getPagedEntities(WebRequestData requestData) { - List entities = dimensionService.getAllDimensions(); - WebMetadata metadata = new WebMetadata(); - - Query filteredQuery = - queryService.getQueryFromUrl( - DimensionalObject.class, requestData.filters(), requestData.orders()); - filteredQuery.setObjects(entities); + private PagedEntities getPagedEntities(GetObjectListParams params) { + Query filteredQuery = queryService.getQueryFromUrl(DimensionalObject.class, params); + filteredQuery.setObjects(dimensionService.getAllDimensions()); - List filteredEntities = + filteredQuery.setSkipPaging(true); // paging is done post + @SuppressWarnings("unchecked") + List filteredNotPaged = (List) queryService.query(filteredQuery); - return PaginationUtils.addPagingIfEnabled(metadata, requestData.options(), filteredEntities); - } - - /** - * This method performs some generic steps. It can be moved into the {@link - * org.hisp.dhis.webapi.controller.AbstractFullReadOnlyController} so other Controllers can use it - * to avoid duplication. - * - * @param rpParameters request parameters - * @return {@link WebRequestData} record purely for data packaging purposes, containing {@link - * WebOptions}, {@link List} of fields and {@link List} of filters - */ - protected WebRequestData applyRequestSetup( - Map rpParameters, OrderParams orderParams) { - - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - List orders = orderParams.getOrders(getSchema(DimensionalObject.class)); - - if (fields.isEmpty()) { - fields.addAll(FieldPreset.defaultPreset().getFields()); - } - - WebOptions options = new WebOptions(rpParameters); - forceFiltering(options, filters); - - return new WebRequestData(options, fields, filters, orders); + return PaginationUtils.addPagingIfEnabled(params, filteredNotPaged); } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionItemPageHandler.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionItemPageHandler.java index ecf4be1f0a53..7cdd65e9a680 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionItemPageHandler.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/dimension/DimensionItemPageHandler.java @@ -36,6 +36,7 @@ import org.hisp.dhis.common.Pager; import org.hisp.dhis.dxf2.common.OrderParams; import org.hisp.dhis.node.types.RootNode; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.service.LinkService; import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.stereotype.Component; @@ -61,7 +62,7 @@ public class DimensionItemPageHandler { * pagination flag must be set to true. See {@link WebOptions#hasPaging(boolean)}. * * @param rootNode the root node where the pagination node will be appended to. - * @param webOptions the WebOptions settings. + * @param params for the paging used * @param dimensionUid the uid of the dimension queried in the API url. See {@link * DimensionController#getItems(String, Map, OrderParams)}. * @param totalOfItems the total of items. This is represented as page total. See {@link @@ -69,14 +70,14 @@ public class DimensionItemPageHandler { */ void addPaginationToNodeIfEnabled( final RootNode rootNode, - final WebOptions webOptions, + final GetObjectListParams params, final String dimensionUid, final int totalOfItems) { - final boolean isPaginationEnabled = webOptions.hasPaging(false); + final boolean isPaginationEnabled = params.isPaging(); if (isPaginationEnabled) { final String apiRelativeUrl = format(RESOURCE_PATH + "/%s/items", dimensionUid); - final Pager pager = new Pager(webOptions.getPage(), totalOfItems, webOptions.getPageSize()); + final Pager pager = new Pager(params.getPage(), totalOfItems, params.getPageSize()); linkService.generatePagerLinks(pager, apiRelativeUrl); rootNode.addChild(createPager(pager)); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventChartController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventChartController.java index 3aa5ec0246d1..15070a26b94d 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventChartController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventChartController.java @@ -37,9 +37,8 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; -import java.util.List; -import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import org.hisp.dhis.common.DimensionService; import org.hisp.dhis.common.IllegalQueryException; import org.hisp.dhis.common.OpenApi; @@ -53,6 +52,8 @@ import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.organisationunit.OrganisationUnitService; import org.hisp.dhis.period.Period; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.system.util.CodecUtils; import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; @@ -60,7 +61,6 @@ import org.hisp.dhis.visualization.PlotData; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.springframework.beans.factory.annotation.Autowired; @@ -80,7 +80,7 @@ @Deprecated @Controller @RequestMapping("/api/eventCharts") -public class EventChartController extends AbstractCrudController { +public class EventChartController extends AbstractCrudController { @Autowired private EventChartService eventChartService; @Autowired private ChartService chartService; @@ -164,14 +164,18 @@ public void getChart( * @deprecated This is a temporary workaround to keep EventChart backward compatible with the new * EventVisualization entity. Only legacy and chart related types can be returned by this * endpoint. Also, multi-program charts cannot be generated, so they are filtered out. - * @param filters */ @Deprecated @Override - protected void forceFiltering(final WebOptions webOptions, final List filters) { - filters.add("type:!eq:PIVOT_TABLE"); - filters.add("type:!eq:LINE_LIST"); - filters.add("legacy:eq:true"); + protected void addProgrammaticModifiers(GetObjectListParams params) { + addProgrammaticFilters(params::addFilter); + } + + @Override + protected void addProgrammaticFilters(Consumer add) { + add.accept("type:!eq:PIVOT_TABLE"); + add.accept("type:!eq:LINE_LIST"); + add.accept("legacy:eq:true"); } @Override @@ -186,8 +190,7 @@ protected void preUpdateEntity(final EventChart eventChart, final EventChart new } @Override - protected void postProcessResponseEntity( - EventChart eventChart, WebOptions options, Map parameters) { + protected void postProcessResponseEntity(EventChart eventChart, GetObjectParams params) { eventChart.populateAnalyticalProperties(); User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventReportController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventReportController.java index d6879917b166..b265b1e17e13 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventReportController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventReportController.java @@ -36,9 +36,8 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; -import java.util.List; -import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import org.hisp.dhis.common.DimensionService; import org.hisp.dhis.common.IllegalQueryException; import org.hisp.dhis.common.OpenApi; @@ -48,10 +47,11 @@ import org.hisp.dhis.i18n.I18nManager; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; import org.hisp.dhis.webapi.controller.AbstractCrudController; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -66,7 +66,8 @@ @Deprecated @Controller @RequestMapping("/api/eventReports") -public class EventReportController extends AbstractCrudController { +public class EventReportController + extends AbstractCrudController { @Autowired private DimensionService dimensionService; @Autowired private I18nManager i18nManager; @@ -103,13 +104,17 @@ protected EventReport deserializeXmlEntity(HttpServletRequest request) throws IO * @deprecated This is a temporary workaround to keep EventReport backward compatible with the new * EventVisualization entity. Only legacy and report related types can be returned by this * endpoint. - * @param filters */ @Deprecated @Override - protected void forceFiltering(final WebOptions webOptions, final List filters) { - filters.add("type:in:[PIVOT_TABLE,LINE_LIST]"); - filters.add("legacy:eq:true"); + protected void addProgrammaticModifiers(GetObjectListParams params) { + addProgrammaticFilters(params::addFilter); + } + + @Override + protected void addProgrammaticFilters(Consumer add) { + add.accept("type:in:[PIVOT_TABLE,LINE_LIST]"); + add.accept("legacy:eq:true"); } @Override @@ -124,8 +129,7 @@ protected void preUpdateEntity(final EventReport eventReport, final EventReport } @Override - protected void postProcessResponseEntity( - EventReport report, WebOptions options, Map parameters) { + protected void postProcessResponseEntity(EventReport report, GetObjectParams params) { report.populateAnalyticalProperties(); User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventVisualizationController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventVisualizationController.java index d2e175403294..39a73a9c89c5 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventVisualizationController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/EventVisualizationController.java @@ -44,7 +44,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -65,6 +64,8 @@ import org.hisp.dhis.period.Period; import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramService; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.user.CurrentUserUtil; import org.hisp.dhis.user.User; import org.hisp.dhis.visualization.ChartService; @@ -72,7 +73,6 @@ import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.springframework.stereotype.Controller; @@ -91,7 +91,8 @@ @ApiVersion({DEFAULT, ALL}) @AllArgsConstructor @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class EventVisualizationController extends AbstractCrudController { +public class EventVisualizationController + extends AbstractCrudController { private final DimensionService dimensionService; private final LegendSetService legendSetService; @@ -161,7 +162,7 @@ protected EventVisualization deserializeXmlEntity(HttpServletRequest request) th @Override protected void postProcessResponseEntity( - EventVisualization eventVisualization, WebOptions options, Map parameters) { + EventVisualization eventVisualization, GetObjectParams params) { eventVisualization.populateAnalyticalProperties(); User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramController.java index 83feef749591..b1b3a4434d59 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramController.java @@ -30,12 +30,12 @@ import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.created; import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.notFound; -import com.google.common.collect.Lists; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.SetValuedMap; @@ -47,15 +47,11 @@ import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ForbiddenException; import org.hisp.dhis.feedback.NotFoundException; -import org.hisp.dhis.fieldfilter.Defaults; import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramService; -import org.hisp.dhis.query.Order; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; -import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.webapi.controller.AbstractCrudController; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -75,47 +71,25 @@ @RequestMapping("/api/programs") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramController extends AbstractCrudController { +public class ProgramController + extends AbstractCrudController { + + @Data + @EqualsAndHashCode(callSuper = true) + public static class GetProgramObjectListParams extends GetObjectListParams { + boolean userFilter; + } + private final ProgramService programService; private final CopyService copyService; @Override - @SuppressWarnings("unchecked") - protected List getEntityList( - WebMetadata metadata, - WebOptions options, - List filters, - List orders, - List objects) - throws QueryParserException { - boolean userFilter = Boolean.parseBoolean(options.getOptions().get("userFilter")); - - List entityList; - Query query = - queryService.getQueryFromUrl( - getEntityClass(), - filters, - orders, - getPaginationData(options), - options.getRootJunction()); - query.setDefaultOrder(); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); - - if (objects == null && options.getOptions().containsKey("query")) { - entityList = - Lists.newArrayList(manager.filter(getEntityClass(), options.getOptions().get("query"))); - } else { - entityList = (List) queryService.query(query); - } - - if (userFilter) { - List programs = programService.getCurrentUserPrograms(); - entityList.retainAll(programs); - metadata.setPager(null); + protected void modifyGetObjectList(GetProgramObjectListParams params, Query query) { + if (params.isUserFilter()) { + query.setSkipSharing(true); + query.setDataSharing(true); } - - return entityList; } @GetMapping("/{uid}/metadata") diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramDataElementController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramDataElementController.java index 11e9a508aab9..04dc42092f5b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramDataElementController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramDataElementController.java @@ -27,33 +27,27 @@ */ package org.hisp.dhis.webapi.controller.event; -import com.google.common.collect.Lists; +import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; + import java.util.List; -import java.util.Map; +import lombok.RequiredArgsConstructor; import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.DimensionalItemObject; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.common.Pager; -import org.hisp.dhis.common.PagerUtils; -import org.hisp.dhis.dxf2.common.OrderParams; +import org.hisp.dhis.common.UID; import org.hisp.dhis.fieldfilter.FieldFilterParams; import org.hisp.dhis.fieldfilter.FieldFilterService; -import org.hisp.dhis.fieldfiltering.FieldPreset; import org.hisp.dhis.node.NodeUtils; import org.hisp.dhis.node.types.RootNode; +import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramDataElementDimensionItem; import org.hisp.dhis.program.ProgramService; -import org.hisp.dhis.query.Order; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.query.Query; import org.hisp.dhis.query.QueryParserException; import org.hisp.dhis.query.QueryService; -import org.hisp.dhis.schema.Schema; -import org.hisp.dhis.schema.SchemaService; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; -import org.hisp.dhis.webapi.service.ContextService; -import org.hisp.dhis.webapi.utils.PaginationUtils; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -63,78 +57,48 @@ /** * @author Lars Helge Overland */ +@OpenApi.EntityType(ProgramDataElementDimensionItem.class) @OpenApi.Document( entity = DimensionalItemObject.class, classifiers = {"team:tracker", "purpose:metadata"}) @Controller @RequestMapping("/api/programDataElements") +@RequiredArgsConstructor @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) public class ProgramDataElementController { - private final QueryService queryService; + private final QueryService queryService; private final FieldFilterService fieldFilterService; - - private final ContextService contextService; - - private final SchemaService schemaService; - private final ProgramService programService; - public ProgramDataElementController( - QueryService queryService, - FieldFilterService fieldFilterService, - ContextService contextService, - SchemaService schemaService, - ProgramService programService) { - this.queryService = queryService; - this.fieldFilterService = fieldFilterService; - this.contextService = contextService; - this.schemaService = schemaService; - this.programService = programService; - } - @GetMapping - @SuppressWarnings("unchecked") public @ResponseBody RootNode getObjectList( - @RequestParam Map rpParameters, OrderParams orderParams) + @OpenApi.Param({UID.class, Program.class}) @RequestParam String program, + GetObjectListParams params) throws QueryParserException { - Schema schema = schemaService.getDynamicSchema(ProgramDataElementDimensionItem.class); - List fields = Lists.newArrayList(contextService.getParameterValues("fields")); - List filters = Lists.newArrayList(contextService.getParameterValues("filter")); - List orders = orderParams.getOrders(schema); - - if (fields.isEmpty()) { - fields.addAll(FieldPreset.ALL.getFields()); + List allItems = + programService.getGeneratedProgramDataElements(program); + + Pager pager = null; + if (params.isPaging()) { + params.setPaging(false); + Query queryForCount = + queryService.getQueryFromUrl(ProgramDataElementDimensionItem.class, params); + queryForCount.setObjects(allItems); + List totalOfItems = queryService.query(queryForCount); + int countTotal = isNotEmpty(totalOfItems) ? totalOfItems.size() : 0; + pager = new Pager(params.getPage(), countTotal, params.getPageSize()); + params.setPaging(true); } - WebOptions options = new WebOptions(rpParameters); - WebMetadata metadata = new WebMetadata(); - - List programDataElements; - Query query = - queryService.getQueryFromUrl( - ProgramDataElementDimensionItem.class, - filters, - orders, - PaginationUtils.getPaginationData(options), - options.getRootJunction()); + Query query = queryService.getQueryFromUrl(ProgramDataElementDimensionItem.class, params); query.setDefaultOrder(); + query.setObjects(allItems); - if (options.contains("program")) { - String programUid = options.get("program"); - programDataElements = programService.getGeneratedProgramDataElements(programUid); - query.setObjects(programDataElements); - } - - programDataElements = (List) queryService.query(query); - - Pager pager = metadata.getPager(); - - if (options.hasPaging() && pager == null) { - pager = new Pager(options.getPage(), programDataElements.size(), options.getPageSize()); - programDataElements = PagerUtils.pageCollection(programDataElements, pager); - } + @SuppressWarnings("unchecked") + List pageItems = + (List) queryService.query(query); RootNode rootNode = NodeUtils.createMetadata(); @@ -142,10 +106,11 @@ public ProgramDataElementController( rootNode.addChild(NodeUtils.createPager(pager)); } + List fields = params.getFields(); + if (fields == null || fields.isEmpty()) fields = List.of("*"); rootNode.addChild( fieldFilterService.toCollectionNode( - ProgramDataElementDimensionItem.class, - new FieldFilterParams(programDataElements, fields))); + ProgramDataElementDimensionItem.class, new FieldFilterParams(pageItems, fields))); return rootNode; } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorController.java index 9eae6a24b4af..8e3a63f021e1 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorController.java @@ -41,6 +41,7 @@ import org.hisp.dhis.i18n.I18nManager; import org.hisp.dhis.program.ProgramIndicator; import org.hisp.dhis.program.ProgramIndicatorService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; @@ -56,7 +57,8 @@ @RequestMapping("/api/programIndicators") @RequiredArgsConstructor @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramIndicatorController extends AbstractCrudController { +public class ProgramIndicatorController + extends AbstractCrudController { private final ProgramIndicatorService programIndicatorService; private final I18nManager i18nManager; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorGroupController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorGroupController.java index 102e6808dbe4..1eaf2f9e22b9 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorGroupController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramIndicatorGroupController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.program.ProgramIndicatorGroup; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -40,4 +41,4 @@ @RequestMapping("/api/programIndicatorGroups") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) public class ProgramIndicatorGroupController - extends AbstractCrudController {} + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleActionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleActionController.java index ee7eef49ca64..86bc2957e99e 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleActionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleActionController.java @@ -40,6 +40,7 @@ import org.hisp.dhis.i18n.I18nManager; import org.hisp.dhis.program.ProgramIndicator; import org.hisp.dhis.programrule.ProgramRuleAction; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.rules.models.RuleValidationResult; import org.hisp.dhis.tracker.imports.programrule.engine.ProgramRuleEngine; import org.hisp.dhis.webapi.controller.AbstractCrudController; @@ -58,7 +59,8 @@ @RequiredArgsConstructor @RequestMapping("/api/programRuleActions") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramRuleActionController extends AbstractCrudController { +public class ProgramRuleActionController + extends AbstractCrudController { private final I18nManager i18nManager; private final ProgramRuleEngine programRuleEngine; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleController.java index 8f36fefa5855..dc1d27783311 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleController.java @@ -40,6 +40,7 @@ import org.hisp.dhis.i18n.I18nManager; import org.hisp.dhis.program.ProgramIndicator; import org.hisp.dhis.programrule.ProgramRule; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.rules.models.RuleValidationResult; import org.hisp.dhis.tracker.imports.programrule.engine.ProgramRuleEngine; import org.hisp.dhis.webapi.controller.AbstractCrudController; @@ -58,7 +59,8 @@ @AllArgsConstructor @RequestMapping("/api/programRules") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramRuleController extends AbstractCrudController { +public class ProgramRuleController + extends AbstractCrudController { private final I18nManager i18nManager; private final ProgramRuleEngine programRuleEngine; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleVariableController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleVariableController.java index 9a99747240e4..4a2cad0dfc17 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleVariableController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramRuleVariableController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.programrule.ProgramRuleVariable; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/programRuleVariables") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramRuleVariableController extends AbstractCrudController {} +public class ProgramRuleVariableController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramSectionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramSectionController.java index 9471bbb92f7e..5df0b4da246b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramSectionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramSectionController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.program.ProgramSection; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/programSections") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramSectionController extends AbstractCrudController {} +public class ProgramSectionController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageController.java index ef3c848fbc87..471b99d59459 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.program.ProgramStage; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/programStages") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramStageController extends AbstractCrudController {} +public class ProgramStageController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageSectionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageSectionController.java index c4367dab271c..e479fbc039fa 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageSectionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/ProgramStageSectionController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.program.ProgramStageSection; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/programStageSections") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramStageSectionController extends AbstractCrudController {} +public class ProgramStageSectionController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/RelationshipTypeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/RelationshipTypeController.java index 1afac30e4630..1f0c28996f23 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/RelationshipTypeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/RelationshipTypeController.java @@ -28,6 +28,7 @@ package org.hisp.dhis.webapi.controller.event; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.relationship.RelationshipType; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/relationshipTypes") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class RelationshipTypeController extends AbstractCrudController {} +public class RelationshipTypeController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityAttributeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityAttributeController.java index b2a6b63e2df5..fd0d5213e8ab 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityAttributeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityAttributeController.java @@ -37,10 +37,13 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dxf2.webmessage.WebMessageException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.reservedvalue.ReserveValueException; import org.hisp.dhis.reservedvalue.ReservedValue; import org.hisp.dhis.reservedvalue.ReservedValueService; @@ -53,7 +56,6 @@ import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.hisp.dhis.webapi.service.ContextService; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; @@ -70,7 +72,9 @@ @RequestMapping("/api/trackedEntityAttributes") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) public class TrackedEntityAttributeController - extends AbstractCrudController { + extends AbstractCrudController< + TrackedEntityAttribute, + TrackedEntityAttributeController.GetTrackedEntityAttributeObjectListParams> { @Autowired private TrackedEntityAttributeService trackedEntityAttributeService; @@ -80,6 +84,12 @@ public class TrackedEntityAttributeController @Autowired private ContextService context; + @Data + @EqualsAndHashCode(callSuper = true) + public static final class GetTrackedEntityAttributeObjectListParams extends GetObjectListParams { + boolean indexableOnly; + } + @GetMapping( value = "/{id}/generateAndReserve", produces = {ContextUtils.CONTENT_TYPE_JSON, ContextUtils.CONTENT_TYPE_JAVASCRIPT}) @@ -167,7 +177,7 @@ private Map getRequiredValues( requiredValues.removeAll(result.keySet()); - if (requiredValues.size() > 0) { + if (!requiredValues.isEmpty()) { throw new WebMessageException( conflict( "Missing required values: " @@ -178,12 +188,11 @@ private Map getRequiredValues( } @Override - protected void forceFiltering(final WebOptions webOptions, final List filters) { - if (webOptions == null || !webOptions.isTrue("indexableOnly")) { - return; - } + protected void addProgrammaticModifiers(GetTrackedEntityAttributeObjectListParams params) { + if (!params.isIndexableOnly()) return; - if (filters.stream().anyMatch(f -> f.startsWith("id:"))) { + List filters = params.getFilters(); + if (filters != null && filters.stream().anyMatch(f -> f.startsWith("id:"))) { throw new IllegalArgumentException( "indexableOnly parameter cannot be set if a separate filter for id is specified"); } @@ -191,13 +200,13 @@ protected void forceFiltering(final WebOptions webOptions, final List fi Set indexableTeas = trackedEntityAttributeService.getAllTrigramIndexableTrackedEntityAttributes(); - StringBuilder sb = new StringBuilder("id:in:"); - sb.append( - indexableTeas.stream() - .map(IdentifiableObject::getUid) - .collect(Collectors.joining(",", "[", "]"))); + String filter = + "id:in:" + + indexableTeas.stream() + .map(IdentifiableObject::getUid) + .collect(Collectors.joining(",", "[", "]")); - filters.add(sb.toString()); + params.addFilter(filter); } private TrackedEntityAttribute getTrackedEntityAttribute(String id) throws WebMessageException { diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityTypeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityTypeController.java index f536571a9c6a..269b56d57c89 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityTypeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/event/TrackedEntityTypeController.java @@ -28,6 +28,7 @@ package org.hisp.dhis.webapi.controller.event; import org.hisp.dhis.common.OpenApi; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.trackedentity.TrackedEntityType; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/trackedEntityTypes") @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class TrackedEntityTypeController extends AbstractCrudController {} +public class TrackedEntityTypeController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java index e9bd0f940f9c..6c4bacd80d01 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorController.java @@ -47,6 +47,7 @@ import org.hisp.dhis.indicator.Indicator; import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -64,7 +65,7 @@ @Slf4j @RequiredArgsConstructor @RequestMapping("/api/indicators") -public class IndicatorController extends AbstractCrudController { +public class IndicatorController extends AbstractCrudController { private final ExpressionService expressionService; private final ExpressionResolverCollection resolvers; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupController.java index e04377a1ca40..4b82e2c4ef31 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupController.java @@ -28,6 +28,7 @@ package org.hisp.dhis.webapi.controller.indicator; import org.hisp.dhis.indicator.IndicatorGroup; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,4 +38,5 @@ */ @Controller @RequestMapping("/api/indicatorGroups") -public class IndicatorGroupController extends AbstractCrudController {} +public class IndicatorGroupController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupSetController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupSetController.java index c2e09ddcc168..e8427918d517 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupSetController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorGroupSetController.java @@ -28,6 +28,7 @@ package org.hisp.dhis.webapi.controller.indicator; import org.hisp.dhis.indicator.IndicatorGroupSet; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,4 +38,5 @@ */ @Controller @RequestMapping("/api/indicatorGroupSets") -public class IndicatorGroupSetController extends AbstractCrudController {} +public class IndicatorGroupSetController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java index 681799e6055b..ac1f15f029f7 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/indicator/IndicatorTypeController.java @@ -39,6 +39,7 @@ import org.hisp.dhis.indicator.IndicatorType; import org.hisp.dhis.merge.MergeParams; import org.hisp.dhis.merge.MergeService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.http.HttpStatus; @@ -56,7 +57,8 @@ @RequestMapping("/api/indicatorTypes") @RequiredArgsConstructor @Slf4j -public class IndicatorTypeController extends AbstractCrudController { +public class IndicatorTypeController + extends AbstractCrudController { private final MergeService indicatorTypeMergeService; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/ExternalMapLayerController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/ExternalMapLayerController.java index 3c82df495da2..ae191cb5ed8a 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/ExternalMapLayerController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/ExternalMapLayerController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.mapping.ExternalMapLayer; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,5 @@ @Controller @RequestMapping("/api/externalMapLayers") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class ExternalMapLayerController extends AbstractCrudController {} +public class ExternalMapLayerController + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapController.java index 3e50e3776988..dea82258b4c8 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapController.java @@ -64,6 +64,8 @@ import org.hisp.dhis.period.Period; import org.hisp.dhis.program.ProgramService; import org.hisp.dhis.program.ProgramStageService; +import org.hisp.dhis.query.GetObjectListParams; +import org.hisp.dhis.query.GetObjectParams; import org.hisp.dhis.schema.MetadataMergeParams; import org.hisp.dhis.trackedentity.TrackedEntityType; import org.hisp.dhis.user.CurrentUser; @@ -73,7 +75,6 @@ import org.hisp.dhis.user.UserService; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; @@ -92,7 +93,7 @@ @Controller @RequestMapping("/api/maps") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class MapController extends AbstractCrudController { +public class MapController extends AbstractCrudController { private static final int MAP_MIN_WIDTH = 140; private static final int MAP_MIN_HEIGHT = 25; @@ -208,8 +209,7 @@ public void getMapData( // -------------------------------------------------------------------------- @Override - public void postProcessResponseEntity( - Map map, WebOptions options, java.util.Map parameters) { + public void postProcessResponseEntity(Map map, GetObjectParams params) { I18nFormat format = i18nManager.getI18nFormat(); User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername()); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapViewController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapViewController.java index 94ff7fd06991..c20b09ea4403 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapViewController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/mapping/MapViewController.java @@ -29,27 +29,20 @@ import static org.hisp.dhis.dxf2.webmessage.WebMessageUtils.notFound; -import com.google.common.collect.Lists; import jakarta.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; -import java.util.List; import javax.imageio.ImageIO; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.common.cache.CacheStrategy; import org.hisp.dhis.dxf2.webmessage.WebMessageException; -import org.hisp.dhis.fieldfilter.Defaults; import org.hisp.dhis.mapgeneration.MapGenerationService; import org.hisp.dhis.mapping.MapView; import org.hisp.dhis.mapping.MappingService; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.organisationunit.OrganisationUnitService; -import org.hisp.dhis.query.Order; -import org.hisp.dhis.query.Query; -import org.hisp.dhis.query.QueryParserException; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.utils.ContextUtils; -import org.hisp.dhis.webapi.webdomain.WebMetadata; -import org.hisp.dhis.webapi.webdomain.WebOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -64,7 +57,7 @@ @Controller @RequestMapping("/api/mapViews") @OpenApi.Document(classifiers = {"team:analytics", "purpose:metadata"}) -public class MapViewController extends AbstractCrudController { +public class MapViewController extends AbstractCrudController { @Autowired private MappingService mappingService; @Autowired private OrganisationUnitService organisationUnitService; @@ -109,40 +102,6 @@ public void getMapView( renderMapViewPng(mapView, response); } - // -------------------------------------------------------------------------- - // Hooks - // -------------------------------------------------------------------------- - - @Override - @SuppressWarnings("unchecked") - protected List getEntityList( - WebMetadata metadata, - WebOptions options, - List filters, - List orders, - List objects) - throws QueryParserException { - List entityList; - Query query = - queryService.getQueryFromUrl( - getEntityClass(), - filters, - orders, - getPaginationData(options), - options.getRootJunction()); - query.setDefaultOrder(); - query.setDefaults(Defaults.valueOf(options.get("defaults", DEFAULTS))); - - if (objects == null && options.getOptions().containsKey("query")) { - entityList = - Lists.newArrayList(manager.filter(getEntityClass(), options.getOptions().get("query"))); - } else { - entityList = (List) queryService.query(query); - } - - return entityList; - } - // -------------------------------------------------------------------------- // Supportive methods // -------------------------------------------------------------------------- diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/message/ProgramMessageController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/message/ProgramMessageController.java index 8cb40bcda07b..6a93abdd7364 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/message/ProgramMessageController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/message/ProgramMessageController.java @@ -45,6 +45,7 @@ import org.hisp.dhis.program.message.ProgramMessageOperationParams; import org.hisp.dhis.program.message.ProgramMessageService; import org.hisp.dhis.program.message.ProgramMessageStatus; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; @@ -60,7 +61,8 @@ @RequestMapping("/api/messages") @ApiVersion({DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) -public class ProgramMessageController extends AbstractCrudController { +public class ProgramMessageController + extends AbstractCrudController { @Autowired private ProgramMessageService programMessageService; @Autowired protected ProgramMessageRequestParamMapper requestParamMapper; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/DataSetNotificationTemplateController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/DataSetNotificationTemplateController.java index 95664fd68445..d16e81422259 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/DataSetNotificationTemplateController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/DataSetNotificationTemplateController.java @@ -30,6 +30,7 @@ import org.hisp.dhis.common.DhisApiVersion; import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.dataset.notifications.DataSetNotificationTemplate; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.hisp.dhis.webapi.mvc.annotation.ApiVersion; import org.springframework.stereotype.Controller; @@ -41,4 +42,4 @@ @ApiVersion(include = {DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) public class DataSetNotificationTemplateController - extends AbstractCrudController {} + extends AbstractCrudController {} diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/ProgramNotificationTemplateController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/ProgramNotificationTemplateController.java index c2d1bddc058d..ffb35a318c54 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/ProgramNotificationTemplateController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/notification/ProgramNotificationTemplateController.java @@ -38,6 +38,7 @@ import org.hisp.dhis.program.notification.ProgramNotificationTemplate; import org.hisp.dhis.program.notification.ProgramNotificationTemplateOperationParams; import org.hisp.dhis.program.notification.ProgramNotificationTemplateService; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.schema.descriptors.ProgramNotificationTemplateSchemaDescriptor; import org.hisp.dhis.security.RequiresAuthority; import org.hisp.dhis.webapi.controller.AbstractCrudController; @@ -56,7 +57,7 @@ @ApiVersion(include = {DhisApiVersion.DEFAULT, DhisApiVersion.ALL}) @OpenApi.Document(classifiers = {"team:tracker", "purpose:metadata"}) public class ProgramNotificationTemplateController - extends AbstractCrudController { + extends AbstractCrudController { private final ProgramNotificationTemplateService programNotificationTemplateService; private final ProgramNotificationTemplateRequestParamsMapper requestParamsMapper; diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/option/OptionController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/option/OptionController.java index ee7ad49ddfb5..2a291fbac7ab 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/option/OptionController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/option/OptionController.java @@ -29,6 +29,7 @@ import org.hisp.dhis.common.OpenApi; import org.hisp.dhis.option.Option; +import org.hisp.dhis.query.GetObjectListParams; import org.hisp.dhis.webapi.controller.AbstractCrudController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,4 @@ @Controller @RequestMapping("/api/options") @OpenApi.Document(classifiers = {"team:platform", "purpose:metadata"}) -public class OptionController extends AbstractCrudController