Skip to content

Commit

Permalink
feat: Create StartDate and EndDate types [DHIS2-16019] (#17454)
Browse files Browse the repository at this point in the history
* feat: Create StartDate and EndDate types [DHIS2-16019]

* Fix docs

* Fix tests

* Fix tests

* Fix review comments

* Fix review comments

* Fix review comments

* Fix review comments

* Fix review comments

* Fix review comments

* Fix review comments

* fix: Remove unused tracker services from program-rule module

* Fix review comments

* Fix review comments

* Fix review comments
  • Loading branch information
enricocolasante committed May 24, 2024
1 parent 81c2249 commit 3c59029
Show file tree
Hide file tree
Showing 14 changed files with 305 additions and 67 deletions.
32 changes: 30 additions & 2 deletions dhis-2/dhis-api/src/main/java/org/hisp/dhis/util/DateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
Expand Down Expand Up @@ -109,6 +111,9 @@ public class DateUtils {
ObjectArrays.concat(
SUPPORTED_DATE_ONLY_PARSERS, SUPPORTED_DATE_TIME_FORMAT_PARSERS, DateTimeParser.class);

private static final DateTimeFormatter ONLY_DATE_FORMATTER =
new DateTimeFormatterBuilder().append(null, SUPPORTED_DATE_ONLY_PARSERS).toFormatter();

private static final DateTimeFormatter DATE_FORMATTER =
new DateTimeFormatterBuilder().append(null, SUPPORTED_DATE_FORMAT_PARSERS).toFormatter();

Expand Down Expand Up @@ -662,8 +667,8 @@ public static String getPrettyInterval(Date start, Date end) {
}

/**
* Parses the given string into a Date using the supported date formats. Returns null if the
* string cannot be parsed.
* Parses the given string into a Date using the supported date formats. Add time at the beginning
* of the day if no time was provided. Returns null if the string cannot be parsed.
*
* @param dateString the date string.
* @return a date.
Expand All @@ -672,6 +677,29 @@ public static Date parseDate(String dateString) {
return safeParseDateTime(dateString, DATE_FORMATTER);
}

/**
* Parses the given string into a Date using the supported date formats. Add time at the end of
* the day if no time was provided. Returns null if the string cannot be parsed.
*
* @param dateString the date string.
* @return a date.
*/
public static Date parseDateEndOfTheDay(String dateString) {
if (StringUtils.isEmpty(dateString)) {
return null;
}

try {
Date date = safeParseDateTime(dateString, ONLY_DATE_FORMATTER);
LocalDateTime localDateTime =
LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).with(LocalTime.MAX);
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
} catch (IllegalArgumentException e) {
// dateString has time defined
}
return safeParseDateTime(dateString, DATE_FORMATTER);
}

/**
* Parses the given string into a Date using the supported date formats. Returns null if the
* string cannot be parsed.
Expand Down
16 changes: 16 additions & 0 deletions dhis-2/dhis-api/src/main/java/org/hisp/dhis/util/ObjectUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ public static <T, U extends RuntimeException> T throwIfNull(T object, Supplier<U
return object;
}

/**
* If object is null return null, otherwise return function applied to object.
*
* @param object to check.
* @param <U> the function return type.
* @param <T> the object type.
* @param function the function to be applied to non-null object.
*/
public static <T, U> U applyIfNotNull(T object, Function<T, U> function) {
if (object == null) {
return null;
}

return function.apply(object);
}

/**
* Util method that always returns a new Set, either instantiated from a non-null Set passed as an
* argument, or if a null arg is passed then returning an empty Set. This helps reduce possible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@
import org.hisp.dhis.notification.logging.NotificationLoggingService;
import org.hisp.dhis.notification.logging.NotificationValidationResult;
import org.hisp.dhis.program.Enrollment;
import org.hisp.dhis.program.EnrollmentService;
import org.hisp.dhis.program.Event;
import org.hisp.dhis.program.EventService;
import org.hisp.dhis.program.notification.ProgramNotificationTemplate;
import org.hisp.dhis.program.notification.ProgramNotificationTemplateService;
import org.hisp.dhis.rules.models.RuleAction;
Expand All @@ -62,10 +60,6 @@ abstract class NotificationRuleActionImplementer implements RuleActionImplemente

protected final NotificationLoggingService notificationLoggingService;

protected final EnrollmentService enrollmentService;

protected final EventService eventService;

protected ExternalNotificationLogEntry createLogEntry(String key, String templateUid) {
ExternalNotificationLogEntry entry = new ExternalNotificationLogEntry();
entry.setLastSentAt(new Date());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
import org.hisp.dhis.notification.logging.NotificationTriggerEvent;
import org.hisp.dhis.notification.logging.NotificationValidationResult;
import org.hisp.dhis.program.Enrollment;
import org.hisp.dhis.program.EnrollmentService;
import org.hisp.dhis.program.Event;
import org.hisp.dhis.program.EventService;
import org.hisp.dhis.program.notification.ProgramNotificationInstance;
import org.hisp.dhis.program.notification.ProgramNotificationInstanceService;
import org.hisp.dhis.program.notification.ProgramNotificationTemplate;
Expand Down Expand Up @@ -70,15 +68,9 @@ public class RuleActionScheduleMessageImplementer extends NotificationRuleAction
public RuleActionScheduleMessageImplementer(
ProgramNotificationTemplateService programNotificationTemplateService,
NotificationLoggingService notificationLoggingService,
EnrollmentService enrollmentService,
EventService eventService,
ProgramNotificationInstanceService programNotificationInstanceService,
NotificationTemplateService notificationTemplateService) {
super(
programNotificationTemplateService,
notificationLoggingService,
enrollmentService,
eventService);
super(programNotificationTemplateService, notificationLoggingService);
this.programNotificationInstanceService = programNotificationInstanceService;
this.notificationTemplateService = notificationTemplateService;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@
import org.hisp.dhis.notification.logging.NotificationTriggerEvent;
import org.hisp.dhis.notification.logging.NotificationValidationResult;
import org.hisp.dhis.program.Enrollment;
import org.hisp.dhis.program.EnrollmentService;
import org.hisp.dhis.program.Event;
import org.hisp.dhis.program.EventService;
import org.hisp.dhis.program.notification.ProgramNotificationTemplate;
import org.hisp.dhis.program.notification.ProgramNotificationTemplateService;
import org.hisp.dhis.program.notification.event.ProgramRuleEnrollmentEvent;
Expand Down Expand Up @@ -72,14 +70,8 @@ public class RuleActionSendMessageImplementer extends NotificationRuleActionImpl
public RuleActionSendMessageImplementer(
ProgramNotificationTemplateService programNotificationTemplateService,
NotificationLoggingService notificationLoggingService,
EnrollmentService enrollmentService,
EventService eventService,
ApplicationEventPublisher publisher) {
super(
programNotificationTemplateService,
notificationLoggingService,
enrollmentService,
eventService);
super(programNotificationTemplateService, notificationLoggingService);
this.publisher = publisher;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@
import org.hisp.dhis.fieldfiltering.FieldPath;
import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.program.Enrollment;
import org.hisp.dhis.program.Event;
import org.hisp.dhis.program.Program;
import org.hisp.dhis.program.ProgramStage;
import org.hisp.dhis.program.ProgramStatus;
import org.hisp.dhis.webapi.controller.event.webrequest.OrderCriteria;
import org.hisp.dhis.webapi.controller.tracker.export.PageRequestParams;
import org.hisp.dhis.webapi.controller.tracker.view.Event;
import org.hisp.dhis.webapi.controller.tracker.view.TrackedEntity;
import org.hisp.dhis.webapi.controller.tracker.view.User;
import org.hisp.dhis.webapi.webdomain.EndDateTime;
import org.hisp.dhis.webapi.webdomain.StartDateTime;

/**
* Represents query parameters sent to {@link EventsExportController}.
Expand Down Expand Up @@ -139,9 +141,9 @@ public class EventRequestParams implements PageRequestParams {

private Date scheduledBefore;

private Date updatedAfter;
private StartDateTime updatedAfter;

private Date updatedBefore;
private EndDateTime updatedBefore;

private String updatedWithin;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
package org.hisp.dhis.webapi.controller.tracker.export.event;

import static java.util.Collections.emptySet;
import static org.hisp.dhis.util.ObjectUtils.applyIfNotNull;
import static org.hisp.dhis.webapi.controller.tracker.export.RequestParamsValidator.parseFilters;
import static org.hisp.dhis.webapi.controller.tracker.export.RequestParamsValidator.validateDeprecatedParameter;
import static org.hisp.dhis.webapi.controller.tracker.export.RequestParamsValidator.validateDeprecatedUidsParameter;
Expand All @@ -48,6 +49,8 @@
import org.hisp.dhis.tracker.export.event.EventOperationParams.EventOperationParamsBuilder;
import org.hisp.dhis.util.DateUtils;
import org.hisp.dhis.webapi.controller.event.webrequest.OrderCriteria;
import org.hisp.dhis.webapi.webdomain.EndDateTime;
import org.hisp.dhis.webapi.webdomain.StartDateTime;
import org.springframework.stereotype.Component;

/**
Expand Down Expand Up @@ -114,22 +117,10 @@ public EventOperationParams map(EventRequestParams eventRequestParams)

EventOperationParamsBuilder builder =
EventOperationParams.builder()
.programUid(
eventRequestParams.getProgram() != null
? eventRequestParams.getProgram().getValue()
: null)
.programStageUid(
eventRequestParams.getProgramStage() != null
? eventRequestParams.getProgramStage().getValue()
: null)
.orgUnitUid(
eventRequestParams.getOrgUnit() != null
? eventRequestParams.getOrgUnit().getValue()
: null)
.trackedEntityUid(
eventRequestParams.getTrackedEntity() != null
? eventRequestParams.getTrackedEntity().getValue()
: null)
.programUid(applyIfNotNull(eventRequestParams.getProgram(), UID::getValue))
.programStageUid(applyIfNotNull(eventRequestParams.getProgramStage(), UID::getValue))
.orgUnitUid(applyIfNotNull(eventRequestParams.getOrgUnit(), UID::getValue))
.trackedEntityUid(applyIfNotNull(eventRequestParams.getTrackedEntity(), UID::getValue))
.programStatus(eventRequestParams.getProgramStatus())
.followUp(eventRequestParams.getFollowUp())
.orgUnitMode(orgUnitMode)
Expand All @@ -139,16 +130,17 @@ public EventOperationParams map(EventRequestParams eventRequestParams)
.occurredBefore(eventRequestParams.getOccurredBefore())
.scheduledAfter(eventRequestParams.getScheduledAfter())
.scheduledBefore(eventRequestParams.getScheduledBefore())
.updatedAfter(eventRequestParams.getUpdatedAfter())
.updatedBefore(eventRequestParams.getUpdatedBefore())
.updatedAfter(
applyIfNotNull(eventRequestParams.getUpdatedAfter(), StartDateTime::toDate))
.updatedBefore(
applyIfNotNull(eventRequestParams.getUpdatedBefore(), EndDateTime::toDate))
.updatedWithin(eventRequestParams.getUpdatedWithin())
.enrollmentEnrolledBefore(eventRequestParams.getEnrollmentEnrolledBefore())
.enrollmentEnrolledAfter(eventRequestParams.getEnrollmentEnrolledAfter())
.enrollmentOccurredBefore(eventRequestParams.getEnrollmentOccurredBefore())
.enrollmentOccurredAfter(eventRequestParams.getEnrollmentOccurredAfter())
.eventStatus(eventRequestParams.getStatus())
.attributeCategoryCombo(
attributeCategoryCombo != null ? attributeCategoryCombo.getValue() : null)
.attributeCategoryCombo(applyIfNotNull(attributeCategoryCombo, UID::getValue))
.attributeCategoryOptions(UID.toValueSet(attributeCategoryOptions))
.idSchemes(eventRequestParams.getIdSchemes())
.includeAttributes(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.webapi.webdomain;

import java.util.Date;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.hisp.dhis.util.DateUtils;

/**
* EndDateTime represents an upper limit date and time used to filter results in search APIs.
*
* <p>EndDateTime accepts any date and time in ISO8601 format. If no time is defined, then the time
* at the end of the day is used by default.
*
* <p>This behavior, combined with {@link StartDateTime}, allows to correctly implement an interval
* search including start and end dates.
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class EndDateTime {
private final Date date;

public static EndDateTime of(String date) {
return new EndDateTime(DateUtils.parseDateEndOfTheDay(date));
}

public Date toDate() {
if (date == null) {
return null;
}
return new Date(date.getTime());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.webapi.webdomain;

import java.util.Date;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.hisp.dhis.util.DateUtils;

/**
* StartDateTime represents a lower limit date and time used to filter results in search APIs.
*
* <p>StartDateTime accepts any date and time in ISO8601 format. If no time is defined, then the
* time at the beginning of the day is used by default.
*
* <p>This behavior, combined with {@link EndDateTime}, allows to correctly implement an interval
* search including start and end dates.
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class StartDateTime {
private final Date date;

public static StartDateTime of(String date) {
return new StartDateTime(DateUtils.parseDate(date));
}

public Date toDate() {
if (date == null) {
return null;
}
return new Date(date.getTime());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ class UIDBindingTest {

@BeforeEach
public void setUp() {
mockMvc =
MockMvcBuilders.standaloneSetup(new UIDController())
.setControllerAdvice(new CrudControllerAdvice())
.build();
mockMvc = MockMvcBuilders.standaloneSetup(new UIDController()).build();
}

@Test
Expand Down
Loading

0 comments on commit 3c59029

Please sign in to comment.