diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/AbstractTrackerPersister.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/AbstractTrackerPersister.java index 91fa826f79ba..3687f0bc2d99 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/AbstractTrackerPersister.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/AbstractTrackerPersister.java @@ -145,6 +145,10 @@ public TrackerTypeReport persist(Session session, TrackerBundle bundle) { } } + if (!bundle.isSkipSideEffects()) { + sideEffectDataBundles.add(handleSideEffects(bundle, convertedDto)); + } + // // Add the entity to the Preheat // @@ -154,10 +158,6 @@ public TrackerTypeReport persist(Session session, TrackerBundle bundle) { session.flush(); } - if (!bundle.isSkipSideEffects()) { - sideEffectDataBundles.add(handleSideEffects(bundle, convertedDto)); - } - bundle.setUpdatedTrackedEntities(updatedTrackedEntities); } catch (Exception e) { final String msg = diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EnrollmentPersister.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EnrollmentPersister.java index 27cdcdb07bdd..25a305f06098 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EnrollmentPersister.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EnrollmentPersister.java @@ -27,8 +27,10 @@ */ package org.hisp.dhis.tracker.bundle.persister; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Objects; import org.hibernate.Session; import org.hisp.dhis.program.ProgramInstance; @@ -42,6 +44,7 @@ import org.hisp.dhis.tracker.converter.TrackerConverterService; import org.hisp.dhis.tracker.converter.TrackerSideEffectConverterService; import org.hisp.dhis.tracker.domain.Enrollment; +import org.hisp.dhis.tracker.job.SideEffectTrigger; import org.hisp.dhis.tracker.job.TrackerSideEffectDataBundle; import org.hisp.dhis.tracker.preheat.TrackerPreheat; import org.springframework.stereotype.Component; @@ -124,6 +127,22 @@ protected boolean isNew(TrackerPreheat preheat, String uid) { @Override protected TrackerSideEffectDataBundle handleSideEffects( TrackerBundle bundle, ProgramInstance programInstance) { + TrackerPreheat preheat = bundle.getPreheat(); + List triggers = new ArrayList<>(); + + if (isNew(preheat, programInstance.getUid())) { + triggers.add(SideEffectTrigger.ENROLLMENT); + if (programInstance.isCompleted()) { + triggers.add(SideEffectTrigger.ENROLLMENT_COMPLETION); + } + } else { + ProgramInstance exitingEnrollment = preheat.getEnrollment(programInstance.getUid()); + if (exitingEnrollment.getStatus() != programInstance.getStatus() + && programInstance.isCompleted()) { + triggers.add(SideEffectTrigger.ENROLLMENT_COMPLETION); + } + } + return TrackerSideEffectDataBundle.builder() .klass(ProgramInstance.class) .enrollmentRuleEffects( @@ -134,6 +153,9 @@ protected TrackerSideEffectDataBundle handleSideEffects( .accessedBy(bundle.getUsername()) .programInstance(programInstance) .program(programInstance.getProgram()) + .programInstance(programInstance) + .program(programInstance.getProgram()) + .triggers(triggers) .build(); } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EventPersister.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EventPersister.java index af47dccc3faa..d869584aac4d 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EventPersister.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/bundle/persister/EventPersister.java @@ -30,9 +30,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.time.Instant; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -59,6 +61,7 @@ import org.hisp.dhis.tracker.converter.TrackerSideEffectConverterService; import org.hisp.dhis.tracker.domain.DataValue; import org.hisp.dhis.tracker.domain.Event; +import org.hisp.dhis.tracker.job.SideEffectTrigger; import org.hisp.dhis.tracker.job.TrackerSideEffectDataBundle; import org.hisp.dhis.tracker.preheat.TrackerPreheat; import org.hisp.dhis.util.DateUtils; @@ -116,6 +119,21 @@ protected boolean isNew(TrackerPreheat preheat, String uid) { @Override protected TrackerSideEffectDataBundle handleSideEffects( TrackerBundle bundle, ProgramStageInstance programStageInstance) { + TrackerPreheat preheat = bundle.getPreheat(); + List triggers = new ArrayList<>(); + + if (isNew(preheat, programStageInstance.getUid())) { + if (programStageInstance.isCompleted()) { + triggers.add(SideEffectTrigger.EVENT_COMPLETION); + } + } else { + ProgramStageInstance existingEvent = preheat.getEvent(programStageInstance.getUid()); + if (existingEvent.getStatus() != programStageInstance.getStatus() + && programStageInstance.isCompleted()) { + triggers.add(SideEffectTrigger.EVENT_COMPLETION); + } + } + return TrackerSideEffectDataBundle.builder() .klass(ProgramStageInstance.class) .enrollmentRuleEffects(new HashMap<>()) @@ -126,6 +144,9 @@ protected TrackerSideEffectDataBundle handleSideEffects( .accessedBy(bundle.getUsername()) .programStageInstance(programStageInstance) .program(programStageInstance.getProgramStage().getProgram()) + .programStageInstance(programStageInstance) + .program(programStageInstance.getProgramStage().getProgram()) + .triggers(triggers) .build(); } diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/SideEffectTrigger.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/SideEffectTrigger.java new file mode 100644 index 000000000000..be5dfbd9606c --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/SideEffectTrigger.java @@ -0,0 +1,38 @@ +/* + * 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.tracker.job; + +/** + * @author Zubair Asghar + */ +public enum SideEffectTrigger { + ENROLLMENT, + ENROLLMENT_COMPLETION, + EVENT_COMPLETION, + NONE +} diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerNotificationThread.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerNotificationThread.java index 5b3ddce86c63..e2ab2c8ce70d 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerNotificationThread.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerNotificationThread.java @@ -27,12 +27,10 @@ */ package org.hisp.dhis.tracker.job; -import com.google.common.collect.ImmutableMap; +import java.util.Map; import java.util.function.Consumer; import org.hisp.dhis.common.BaseIdentifiableObject; import org.hisp.dhis.common.IdentifiableObjectManager; -import org.hisp.dhis.program.ProgramInstance; -import org.hisp.dhis.program.ProgramStageInstance; import org.hisp.dhis.program.notification.ProgramNotificationService; import org.hisp.dhis.security.SecurityContextRunnable; import org.hisp.dhis.system.notification.NotificationLevel; @@ -52,30 +50,25 @@ public class TrackerNotificationThread extends SecurityContextRunnable { private final Notifier notifier; - private ProgramNotificationService programNotificationService; - private TrackerSideEffectDataBundle sideEffectDataBundle; private IdentifiableObjectManager manager; - private final ImmutableMap, Consumer> - serviceMapper = - new ImmutableMap.Builder, Consumer>() - .put( - ProgramInstance.class, - id -> programNotificationService.sendEnrollmentNotifications(id)) - .put( - ProgramStageInstance.class, - id -> programNotificationService.sendEventCompletionNotifications(id)) - .build(); + private final Map> serviceMapper; public TrackerNotificationThread( ProgramNotificationService programNotificationService, Notifier notifier, IdentifiableObjectManager manager) { - this.programNotificationService = programNotificationService; this.notifier = notifier; this.manager = manager; + this.serviceMapper = + Map.of( + SideEffectTrigger.ENROLLMENT, programNotificationService::sendEnrollmentNotifications, + SideEffectTrigger.EVENT_COMPLETION, + programNotificationService::sendEventCompletionNotifications, + SideEffectTrigger.ENROLLMENT_COMPLETION, + programNotificationService::sendEnrollmentCompletionNotifications); } @Override @@ -84,11 +77,14 @@ public void call() { return; } - if (serviceMapper.containsKey(sideEffectDataBundle.getKlass())) { - BaseIdentifiableObject object = - manager.get(sideEffectDataBundle.getKlass(), sideEffectDataBundle.getObject()); - - serviceMapper.get(sideEffectDataBundle.getKlass()).accept(object.getId()); + for (SideEffectTrigger trigger : sideEffectDataBundle.getTriggers()) { + if (serviceMapper.containsKey(trigger)) { + BaseIdentifiableObject object = + manager.get(sideEffectDataBundle.getKlass(), sideEffectDataBundle.getObject()); + if (object != null) { + serviceMapper.get(trigger).accept(object.getId()); + } + } } notifier.notify( diff --git a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerSideEffectDataBundle.java b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerSideEffectDataBundle.java index 1f8c49537cb9..3174daf1d40f 100644 --- a/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerSideEffectDataBundle.java +++ b/dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/tracker/job/TrackerSideEffectDataBundle.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -68,6 +69,9 @@ public class TrackerSideEffectDataBundle implements Message { @JsonProperty private ProgramStageInstance programStageInstance; + @JsonProperty + private List triggers = new ArrayList<>(); + @JsonProperty @Builder.Default private Map> enrollmentRuleEffects = new HashMap<>(); diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/TrackerSideEffectHandlerServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/TrackerSideEffectHandlerServiceTest.java index a15a9a9ef679..8544c3acbd96 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/TrackerSideEffectHandlerServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/tracker/bundle/TrackerSideEffectHandlerServiceTest.java @@ -27,46 +27,291 @@ */ package org.hisp.dhis.tracker.bundle; +import static org.awaitility.Awaitility.await; import static org.hisp.dhis.tracker.Assertions.assertNoErrors; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.hisp.dhis.utils.Assertions.assertContainsOnly; import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.hisp.dhis.common.CodeGenerator; import org.hisp.dhis.common.IdentifiableObjectManager; -import org.hisp.dhis.program.notification.ProgramNotificationInstance; +import org.hisp.dhis.event.EventStatus; +import org.hisp.dhis.message.MessageConversation; +import org.hisp.dhis.organisationunit.OrganisationUnit; +import org.hisp.dhis.program.Program; +import org.hisp.dhis.program.ProgramStage; +import org.hisp.dhis.program.notification.NotificationTrigger; +import org.hisp.dhis.program.notification.ProgramNotificationRecipient; +import org.hisp.dhis.program.notification.ProgramNotificationTemplate; +import org.hisp.dhis.test.integration.IntegrationTestBase; +import org.hisp.dhis.trackedentity.TrackedEntityInstance; +import org.hisp.dhis.trackedentity.TrackedEntityType; +import org.hisp.dhis.tracker.TrackerImportParams; import org.hisp.dhis.tracker.TrackerImportService; -import org.hisp.dhis.tracker.TrackerTest; +import org.hisp.dhis.tracker.TrackerImportStrategy; +import org.hisp.dhis.tracker.domain.EnrollmentStatus; +import org.hisp.dhis.tracker.domain.MetadataIdentifier; import org.hisp.dhis.tracker.report.TrackerImportReport; -import org.junit.jupiter.api.Disabled; +import org.hisp.dhis.user.User; +import org.hisp.dhis.user.UserGroup; +import org.hisp.dhis.user.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; /** * @author Zubair Asghar */ -class TrackerSideEffectHandlerServiceTest extends TrackerTest { +class TrackerSideEffectHandlerServiceTest extends IntegrationTestBase { + @Autowired private TrackerImportService trackerImportService; @Autowired private IdentifiableObjectManager manager; + @Autowired protected UserService _userService; + + private Program programA; + + private ProgramStage programStageA; + + private OrganisationUnit orgUnitA; + + private TrackedEntityType trackedEntityTypeA; + + private TrackedEntityInstance trackedEntityA; + + private ProgramNotificationTemplate templateForEnrollmentCompletion; + private ProgramNotificationTemplate templateForEnrollment; + private ProgramNotificationTemplate templateForEventCompletion; + + private User user; + private UserGroup userGroup; + @Override - protected void initTest() throws IOException { - setUpMetadata("tracker/tracker_metadata_with_program_rules.json"); - injectAdminUser(); + protected void setUpTest() throws IOException { + userService = _userService; + + orgUnitA = createOrganisationUnit('A'); + manager.save(orgUnitA, false); + + trackedEntityTypeA = createTrackedEntityType('A'); + manager.save(trackedEntityTypeA, false); + + programA = createProgram('P', new HashSet<>(), orgUnitA); + programA.setTrackedEntityType(trackedEntityTypeA); + manager.save(programA, false); + + programStageA = createProgramStage('S', programA); + manager.save(programStageA, false); + + programA.getProgramStages().add(programStageA); + manager.update(programA); + + trackedEntityA = createTrackedEntityInstance('T', orgUnitA); + trackedEntityA.setTrackedEntityType(trackedEntityTypeA); + manager.save(trackedEntityA, false); + + user = createAndAddUser(false, "user", Set.of(orgUnitA), Set.of(orgUnitA), "ALL"); + + userGroup = createUserGroup('U', Set.of(user)); + manager.save(userGroup, false); + + user.getGroups().add(userGroup); + manager.update(user); + + templateForEnrollment = + createProgramNotification( + "enrollment", + CodeGenerator.generateUid(), + "enrollment_subject", + NotificationTrigger.ENROLLMENT); + templateForEnrollmentCompletion = + createProgramNotification( + "enrollment_completion", + CodeGenerator.generateUid(), + "enrollment_completion_subject", + NotificationTrigger.COMPLETION); + templateForEventCompletion = + createProgramNotification( + "event_completion", + CodeGenerator.generateUid(), + "event_completion_subject", + NotificationTrigger.COMPLETION); + + manager.save(templateForEnrollmentCompletion); + manager.save(templateForEnrollment); + manager.save(templateForEventCompletion); + + programA.getNotificationTemplates().add(templateForEnrollmentCompletion); + programA.getNotificationTemplates().add(templateForEnrollment); + programStageA.getNotificationTemplates().add(templateForEventCompletion); + + manager.update(programA); + manager.update(programStageA); } @Test - @Disabled("Needs to be added once rule engine PR is merged") - void testRuleEngineSideEffectHandlerService() throws IOException { - TrackerImportReport trackerImportReport = + void shouldSendTrackerNotificationAtEnrollmentCompletionAndThenEventCompletion() { + org.hisp.dhis.tracker.domain.Enrollment enrollment = + org.hisp.dhis.tracker.domain.Enrollment.builder() + .program(MetadataIdentifier.ofUid(programA.getUid())) + .orgUnit(MetadataIdentifier.ofUid(orgUnitA.getUid())) + .trackedEntity(trackedEntityA.getUid()) + .status(EnrollmentStatus.COMPLETED) + .enrolledAt(Instant.now()) + .occurredAt(Instant.now()) + .enrollment(CodeGenerator.generateUid()) + .build(); + + TrackerImportReport importReport = trackerImportService.importTracker( - fromJson("tracker/enrollment_data_with_program_rule_side_effects.json")); - assertNoErrors(trackerImportReport); + TrackerImportParams.builder() + .userId(user.getUid()) + .importStrategy(TrackerImportStrategy.CREATE_AND_UPDATE) + .enrollments(List.of(enrollment)) + .build()); + + assertNoErrors(importReport); + + await() + .atMost(3, TimeUnit.SECONDS) + .until(() -> manager.getAll(MessageConversation.class).size() > 1); + + List messageConversations = manager.getAll(MessageConversation.class); + + List subjectMessages = new ArrayList<>(); + for (MessageConversation messageConversation : messageConversations) { + String subject = messageConversation.getSubject(); + subjectMessages.add(subject); + } + + assertContainsOnly( + List.of("enrollment_subject", "enrollment_completion_subject"), subjectMessages); + + org.hisp.dhis.tracker.domain.Event event = + org.hisp.dhis.tracker.domain.Event.builder() + .program(MetadataIdentifier.ofUid(programA.getUid())) + .orgUnit(MetadataIdentifier.ofUid(orgUnitA.getUid())) + .enrollment(enrollment.getEnrollment()) + .event(CodeGenerator.generateUid()) + .programStage(MetadataIdentifier.ofUid(programStageA.getUid())) + .status(EventStatus.COMPLETED) + .attributeOptionCombo(MetadataIdentifier.EMPTY_UID) + .completedAt(Instant.now()) + .occurredAt(Instant.now()) + .build(); + + importReport = + trackerImportService.importTracker( + TrackerImportParams.builder() + .userId(user.getUid()) + .importStrategy(TrackerImportStrategy.CREATE_AND_UPDATE) + .events(List.of(event)) + .build()); + + assertNoErrors(importReport); + + await() + .atMost(3, TimeUnit.SECONDS) + .until(() -> manager.getAll(MessageConversation.class).size() > 2); + + messageConversations = manager.getAll(MessageConversation.class); + + List list = new ArrayList<>(); + for (MessageConversation messageConversation : messageConversations) { + String subject = messageConversation.getSubject(); + list.add(subject); + } + subjectMessages = list; + + assertContainsOnly( + List.of("enrollment_subject", "enrollment_completion_subject", "event_completion_subject"), + subjectMessages); + } + + @Test + void shouldSendEnrollmentCompletionNotificationOnlyOnce() { + String uid = CodeGenerator.generateUid(); + org.hisp.dhis.tracker.domain.Enrollment enrollment = + org.hisp.dhis.tracker.domain.Enrollment.builder() + .program(MetadataIdentifier.ofUid(programA.getUid())) + .orgUnit(MetadataIdentifier.ofUid(orgUnitA.getUid())) + .trackedEntity(trackedEntityA.getUid()) + .status(EnrollmentStatus.COMPLETED) + .enrollment(uid) + .enrolledAt(Instant.now()) + .occurredAt(Instant.now()) + .enrollment(CodeGenerator.generateUid()) + .build(); + + TrackerImportReport importReport = + trackerImportService.importTracker( + TrackerImportParams.builder() + .userId(user.getUid()) + .importStrategy(TrackerImportStrategy.CREATE_AND_UPDATE) + .enrollments(List.of(enrollment)) + .build()); + + assertNoErrors(importReport); + + await() + .atMost(3, TimeUnit.SECONDS) + .until(() -> manager.getAll(MessageConversation.class).size() > 1); + + List messageConversations = manager.getAll(MessageConversation.class); + + List subjectMessages = new ArrayList<>(); + for (MessageConversation messageConversation : messageConversations) { + String subject = messageConversation.getSubject(); + subjectMessages.add(subject); + } + + assertContainsOnly( + List.of("enrollment_subject", "enrollment_completion_subject"), subjectMessages); + + importReport = + trackerImportService.importTracker( + TrackerImportParams.builder() + .userId(user.getUid()) + .importStrategy(TrackerImportStrategy.CREATE_AND_UPDATE) + .enrollments(List.of(enrollment)) + .build()); + + assertNoErrors(importReport); + + await() + .atMost(3, TimeUnit.SECONDS) + .until(() -> manager.getAll(MessageConversation.class).size() > 1); + + messageConversations = manager.getAll(MessageConversation.class); + + List list = new ArrayList<>(); + for (MessageConversation messageConversation : messageConversations) { + String subject = messageConversation.getSubject(); + list.add(subject); + } + subjectMessages = list; + + assertContainsOnly( + List.of("enrollment_subject", "enrollment_completion_subject"), subjectMessages); + } + + private ProgramNotificationTemplate createProgramNotification( + String name, String uid, String subject, NotificationTrigger trigger) { + ProgramNotificationTemplate template = new ProgramNotificationTemplate(); + template.setAutoFields(); + template.setUid(uid); + template.setName(name); + template.setNotificationTrigger(trigger); + template.setMessageTemplate("message_text"); + template.setSubjectTemplate(subject); + template.setNotificationRecipient(ProgramNotificationRecipient.USER_GROUP); + template.setRecipientUserGroup(userGroup); - List instances = manager.getAll(ProgramNotificationInstance.class); - assertFalse(instances.isEmpty()); - ProgramNotificationInstance instance = instances.get(0); - assertEquals("FdIeUL4gyoB", instance.getProgramNotificationTemplateSnapshot().getUid()); + return template; } }