blob: 1eb03718aa52d3a397429c27f779c6181f8fc063 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package org.apache.unomi.itests;
import org.apache.unomi.api.Event;
import org.apache.unomi.api.Metadata;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.exceptions.BadSegmentConditionException;
import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.api.segments.Scoring;
import org.apache.unomi.api.segments.ScoringElement;
import org.apache.unomi.api.segments.Segment;
import org.apache.unomi.api.services.EventService;
import org.apache.unomi.api.services.ProfileService;
import org.apache.unomi.api.services.SegmentService;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerSuite;
import org.ops4j.pax.exam.util.Filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerSuite.class)
public class SegmentIT extends BaseIT {
private final static Logger LOGGER = LoggerFactory.getLogger(SegmentIT.class);
private final static String SEGMENT_ID = "test-segment-id-2";
@Inject
@Filter(timeout = 600000)
protected SegmentService segmentService;
@Inject
@Filter(timeout = 600000)
protected ProfileService profileService;
@Inject
@Filter(timeout = 600000)
protected EventService eventService;
@Inject
@Filter(timeout = 600000)
protected PersistenceService persistenceService;
@Before
public void setUp() throws InterruptedException {
removeItems(Segment.class);
removeItems(Scoring.class);
}
@After
public void tearDown() throws InterruptedException {
removeItems(Segment.class);
removeItems(Scoring.class);
removeItems(Profile.class);
removeItems(Event.class);
}
@Test
public void testSegments() {
Assert.assertNotNull("Segment service should be available", segmentService);
List<Metadata> segmentMetadatas = segmentService.getSegmentMetadatas(0, 50, null).getList();
Assert.assertEquals("Segment metadata list should be empty", 0, segmentMetadatas.size());
LOGGER.info("Retrieved " + segmentMetadatas.size() + " segment metadata entries");
}
@Test(expected = BadSegmentConditionException.class)
public void testSegmentWithNullCondition() {
Metadata segmentMetadata = new Metadata(SEGMENT_ID);
Segment segment = new Segment();
segment.setMetadata(segmentMetadata);
segment.setCondition(null);
segmentService.setSegmentDefinition(segment);
}
@Test
public void testSegmentWithNullConditionButDisabled() {
Metadata segmentMetadata = new Metadata(SEGMENT_ID);
segmentMetadata.setEnabled(false);
Segment segment = new Segment();
segment.setMetadata(segmentMetadata);
segment.setCondition(null);
segmentService.setSegmentDefinition(segment);
segmentService.removeSegmentDefinition(SEGMENT_ID, false);
}
@Test(expected = BadSegmentConditionException.class)
public void testSegmentWithInValidCondition() {
Metadata segmentMetadata = new Metadata(SEGMENT_ID);
Segment segment = new Segment();
segment.setMetadata(segmentMetadata);
Condition condition = new Condition();
condition.setParameter("param", "param value");
condition.setConditionTypeId("fakeConditionId");
segment.setCondition(condition);
segmentService.setSegmentDefinition(segment);
}
@Test(expected = BadSegmentConditionException.class)
public void testSegmentWithInvalidConditionParameterTypes() {
Metadata segmentMetadata = new Metadata(SEGMENT_ID);
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
segmentCondition.setParameter("minimumEventCount", "2");
segmentCondition.setParameter("numberOfDays", "10");
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
segmentCondition.setParameter("eventCondition", pastEventEventCondition);
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
}
@Test
public void testSegmentWithValidCondition() {
Metadata segmentMetadata = new Metadata(SEGMENT_ID);
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
segmentCondition.setParameter("minimumEventCount", 2);
segmentCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
segmentCondition.setParameter("eventCondition", pastEventEventCondition);
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
segmentService.removeSegmentDefinition(SEGMENT_ID, false);
}
@Test
public void testProfileEngagedSegmentAddedRemoved() throws InterruptedException {
Condition segmentSearchCondition = new Condition();
segmentSearchCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition"));
segmentSearchCondition.setParameter("propertyName", "segments");
segmentSearchCondition.setParameter("comparisonOperator", "equals");
segmentSearchCondition.setParameter("propertyValue", "add-delete-segment-test");
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profile.setProperty("age", 42);
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should not be engaged in the segment yet", () -> persistenceService.query(segmentSearchCondition, null, Profile.class),
profiles -> profiles.size() == 0, 1000, 20);
// create the segment
Metadata segmentMetadata = new Metadata("add-delete-segment-test");
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition"));
segmentCondition.setParameter("propertyName", "properties.age");
segmentCondition.setParameter("comparisonOperator", "exists");
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
// insure the profile that did the past event condition is correctly engaged in the segment.
keepTrying("Profile should be engaged in the segment", () -> persistenceService.query(segmentSearchCondition, null, Profile.class),
profiles -> profiles.size() == 1, 1000, 20);
// delete the segment
segmentService.removeSegmentDefinition("add-delete-segment-test", false);
// insure the profile is not engaged anymore after segment deleted
keepTrying("Profile should not be engaged in the segment anymore after the segment have been deleted", () -> persistenceService.query(segmentSearchCondition, null, Profile.class),
profiles -> profiles.size() == 0, 1000, 20);
}
@Test
public void testSegmentWithPastEventCondition() throws InterruptedException {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// send event for profile from a previous date (today -3 days)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
int changes = eventService.send(testEvent);
if ((changes & EventService.PROFILE_UPDATED) == EventService.PROFILE_UPDATED) {
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null);
}
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// create the segment
Metadata segmentMetadata = new Metadata("past-event-segment-test");
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
segmentCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
segmentCondition.setParameter("eventCondition", pastEventEventCondition);
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
// insure the profile that did the past event condition is correctly engaged in the segment.
keepTrying("Profile should be engaged in the segment", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getSegments().contains("past-event-segment-test"), 1000, 20);
}
@Test
public void testSegmentWithNegativePastEventCondition() throws InterruptedException {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// create the negative past event condition segment
Metadata segmentMetadata = new Metadata("negative-past-event-segment-test");
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
segmentCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "negative-test-event-type");
segmentCondition.setParameter("eventCondition", pastEventEventCondition);
segmentCondition.setParameter("operator", "eventsNotOccurred");
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
// insure that profile is correctly engaged in sement since there is no events yet.
keepTrying("Profile should be engaged in the segment, there is no event for the past condition yet",
() -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getSegments().contains("negative-past-event-segment-test"), 1000, 20);
// we load the profile so that we are sure that it contains the segments
profile = profileService.load("test_profile_id");
// send event for profile from a previous date (today -3 days)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("negative-test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
int changes = eventService.send(testEvent);
if ((changes & EventService.PROFILE_UPDATED) == EventService.PROFILE_UPDATED) {
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null);
}
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// now Profile should be out of the segment since one event have been done and the past event is only valid for no events occurrences
keepTrying("Profile should not be engaged in the segment anymore, it have a least one event now",
() -> profileService.load("test_profile_id"),
updatedProfile -> !updatedProfile.getSegments().contains("negative-past-event-segment-test"), 1000, 20);
}
@Test
public void testSegmentPastEventRecalculation() throws Exception {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// create the segment
Metadata segmentMetadata = new Metadata("past-event-segment-test");
Segment segment = new Segment(segmentMetadata);
Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
segmentCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
segmentCondition.setParameter("eventCondition", pastEventEventCondition);
segment.setCondition(segmentCondition);
segmentService.setSegmentDefinition(segment);
Thread.sleep(5000);
profile = profileService.load("test_profile_id");
// Persist the event (do not send it into the system so that it will not be processed by the rules)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
persistenceService.save(testEvent, null, true);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// insure the profile is not yet engaged since we directly saved the event in ES
profile = profileService.load("test_profile_id");
Assert.assertFalse("Profile should not be engaged in the segment", profile.getSegments().contains("past-event-segment-test"));
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should be engaged in the segment", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getSegments().contains("past-event-segment-test"), 1000, 20);
// update the event to a date out of the past event condition
removeItems(Event.class);
localDate = LocalDate.now().minusDays(15);
testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
persistenceService.save(testEvent);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should not be engaged in the segment anymore", () -> profileService.load("test_profile_id"),
updatedProfile -> !updatedProfile.getSegments().contains("past-event-segment-test"), 1000, 20);
}
@Test
public void testScoringWithPastEventCondition() throws InterruptedException {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// send event for profile from a previous date (today -3 days)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
int changes = eventService.send(testEvent);
if ((changes & EventService.PROFILE_UPDATED) == EventService.PROFILE_UPDATED) {
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null);
}
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// create the past event condition
Condition pastEventCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
pastEventCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
pastEventCondition.setParameter("eventCondition", pastEventEventCondition);
// create the scoring plan
Metadata scoringMetadata = new Metadata("past-event-scoring-test");
Scoring scoring = new Scoring(scoringMetadata);
List<ScoringElement> scoringElements = new ArrayList<>();
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(pastEventCondition);
scoringElement.setValue(50);
scoringElements.add(scoringElement);
scoring.setElements(scoringElements);
segmentService.setScoringDefinition(scoring);
// insure the profile that did the past event condition is correctly engaged in the scoring plan.
keepTrying("Profile should be engaged in the scoring with a score of 50", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getScores() != null && updatedProfile.getScores().containsKey("past-event-scoring-test")
&& updatedProfile.getScores().get("past-event-scoring-test") == 50, 1000, 20);
}
@Test
public void testScoringPastEventRecalculation() throws Exception {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// create the past event condition
Condition pastEventCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
pastEventCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
pastEventCondition.setParameter("eventCondition", pastEventEventCondition);
// create the scoring
Metadata scoringMetadata = new Metadata("past-event-scoring-test");
Scoring scoring = new Scoring(scoringMetadata);
List<ScoringElement> scoringElements = new ArrayList<>();
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(pastEventCondition);
scoringElement.setValue(50);
scoringElements.add(scoringElement);
scoring.setElements(scoringElements);
segmentService.setScoringDefinition(scoring);
Thread.sleep(5000);
// Persist the event (do not send it into the system so that it will not be processed by the rules)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
persistenceService.save(testEvent, null, true);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// insure the profile is not yet engaged since we directly saved the event in ES
profile = profileService.load("test_profile_id");
Assert.assertTrue("Profile should not be engaged in the scoring",
profile.getScores() == null || !profile.getScores().containsKey("past-event-scoring-test"));
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should be engaged in the scoring with a score of 50", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getScores() != null && updatedProfile.getScores().containsKey("past-event-scoring-test")
&& updatedProfile.getScores().get("past-event-scoring-test") == 50, 1000, 20);
// update the event to a date out of the past event condition
removeItems(Event.class);
localDate = LocalDate.now().minusDays(15);
testEvent = new Event("test-event-type", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
persistenceService.save(testEvent);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should not be engaged in the scoring anymore", () -> profileService.load("test_profile_id"),
updatedProfile -> !updatedProfile.getScores().containsKey("past-event-scoring-test"), 1000, 20);
}
@Test
public void testScoringPastEventRecalculationMaximumEventCount() throws Exception {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// create the past event condition
Condition pastEventCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
pastEventCondition.setParameter("numberOfDays", 10);
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type-max");
pastEventCondition.setParameter("eventCondition", pastEventEventCondition);
pastEventCondition.setParameter("maximumEventCount", 1);
// create the scoring
Metadata scoringMetadata = new Metadata("past-event-scoring-test-max");
Scoring scoring = new Scoring(scoringMetadata);
List<ScoringElement> scoringElements = new ArrayList<>();
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(pastEventCondition);
scoringElement.setValue(50);
scoringElements.add(scoringElement);
scoring.setElements(scoringElements);
segmentService.setScoringDefinition(scoring);
Thread.sleep(5000);
// Persist the event (do not send it into the system so that it will not be processed by the rules)
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
Event testEvent = new Event("test-event-type-max", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
persistenceService.save(testEvent, null, true);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// insure the profile is not yet engaged since we directly saved the event in ES
profile = profileService.load("test_profile_id");
Assert.assertTrue("Profile should not be engaged in the scoring",
profile.getScores() == null || !profile.getScores().containsKey("past-event-scoring-test-max"));
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should be engaged in the scoring with a score of 50", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getScores() != null && updatedProfile.getScores()
.containsKey("past-event-scoring-test-max") && updatedProfile.getScores().get("past-event-scoring-test-max") == 50,
1000, 20);
// Persist the 2 event (do not send it into the system so that it will not be processed by the rules)
defaultZoneId = ZoneId.systemDefault();
localDate = LocalDate.now().minusDays(3);
testEvent = new Event("test-event-type-max", null, profile, null, null, profile,
Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
testEvent.setPersistent(true);
persistenceService.save(testEvent, null, true);
persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should not be engaged in the scoring anymore", () -> profileService.load("test_profile_id"),
updatedProfile -> !updatedProfile.getScores().containsKey("past-event-scoring-test-max"), 1000, 20);
}
@Test
public void testScoringRecalculation() throws Exception {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
Date timestampEventInRange = new SimpleDateFormat("yyyy-MM-dd").parse("2000-10-30");
// create the past event condition
Condition pastEventCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
pastEventCondition.setParameter("minimumEventCount", 1);
pastEventCondition.setParameter("maximumEventCount", 2);
pastEventCondition.setParameter("fromDate", "2000-07-15T07:00:00Z");
pastEventCondition.setParameter("toDate", "2001-01-15T07:00:00Z");
;
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
pastEventCondition.setParameter("eventCondition", pastEventEventCondition);
// create the scoring
Metadata scoringMetadata = new Metadata("past-event-scoring-test");
Scoring scoring = new Scoring(scoringMetadata);
List<ScoringElement> scoringElements = new ArrayList<>();
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(pastEventCondition);
scoringElement.setValue(50);
scoringElements.add(scoringElement);
scoring.setElements(scoringElements);
segmentService.setScoringDefinition(scoring);
refreshPersistence();
// Send 2 events that match the scoring plan.
profile = profileService.load("test_profile_id");
Event testEvent = new Event("test-event-type", null, profile, null, null, profile, timestampEventInRange);
testEvent.setPersistent(true);
eventService.send(testEvent);
refreshPersistence();
// 2nd event
testEvent = new Event("test-event-type", null, testEvent.getProfile(), null, null, testEvent.getProfile(), timestampEventInRange);
eventService.send(testEvent);
refreshPersistence();
// insure the profile is engaged;
try {
Assert.assertTrue("Profile should have 2 events in the scoring",
(Long) ((Map) testEvent.getProfile().getSystemProperties().get("pastEvents"))
.get(pastEventCondition.getParameterValues().get("generatedPropertyKey")) == 2);
Assert.assertTrue("Profile is engaged", testEvent.getProfile().getScores().containsKey("past-event-scoring-test")
&& testEvent.getProfile().getScores().get("past-event-scoring-test") == 50);
} catch (Exception e) {
Assert.fail("Unable to read past event because " + e.getMessage());
}
profileService.save(testEvent.getProfile());
refreshPersistence();
// recalculate event conditions
segmentService.recalculatePastEventConditions();
// insure the profile is still engaged after recalculate;
keepTrying("Profile should have 2 events in the scoring", () -> profileService.load("test_profile_id"), updatedProfile -> {
try {
boolean eventCounted = (Integer) ((Map) updatedProfile.getSystemProperties().get("pastEvents"))
.get(pastEventCondition.getParameterValues().get("generatedPropertyKey")) == 2;
boolean profileEngaged = updatedProfile.getScores().containsKey("past-event-scoring-test")
&& updatedProfile.getScores().get("past-event-scoring-test") == 50;
return eventCounted && profileEngaged;
} catch (Exception e) {
// Do nothing, unable to read value
}
;
return false;
}, 1000, 20);
// Add one more event
testEvent = new Event("test-event-type", null, testEvent.getProfile(), null, null, testEvent.getProfile(), timestampEventInRange);
eventService.send(testEvent);
// As 3 events have match, the profile should not be part of the scoring plan.
try {
Assert.assertTrue("Profile should have no scoring", testEvent.getProfile().getScores().get("past-event-scoring-test") == 0);
} catch (Exception e) {
Assert.fail("Unable to read past event because " + e.getMessage());
}
profileService.save(testEvent.getProfile());
refreshPersistence();
// now recalculate the past event conditions
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
// As 3 events have match, the profile should not be part of the scoring plan.
keepTrying("Profile should not be part of the scoring anymore", () -> profileService.load("test_profile_id"), updatedProfile -> {
try {
return updatedProfile.getScores().get("past-event-scoring-test") == 0;
} catch (Exception e) {
// Do nothing, unable to read value
}
;
return false;
}, 1000, 20);
}
@Test
public void testLinkedItems() throws Exception {
// create the past event condition
Condition pastEventCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
pastEventCondition.setParameter("minimumEventCount", 1);
pastEventCondition.setParameter("maximumEventCount", 2);
pastEventCondition.setParameter("fromDate", "2000-07-15T07:00:00Z");
pastEventCondition.setParameter("toDate", "2001-01-15T07:00:00Z");
;
Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
pastEventEventCondition.setParameter("eventTypeId", "test-event-type");
pastEventCondition.setParameter("eventCondition", pastEventEventCondition);
// create the scoring
Metadata scoringMetadata = new Metadata("past-event-scoring-test");
Scoring scoring = new Scoring(scoringMetadata);
List<ScoringElement> scoringElements = new ArrayList<>();
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(pastEventCondition);
scoringElement.setValue(50);
scoringElements.add(scoringElement);
scoring.setElements(scoringElements);
segmentService.setScoringDefinition(scoring);
refreshPersistence();
// Check linkedItems
List<Rule> rules = persistenceService.getAllItems(Rule.class);
Rule scoringRule = rules.stream().filter(rule -> rule.getItemId().equals(pastEventCondition.getParameter("generatedPropertyKey")))
.findFirst().get();
Assert.assertEquals("Scoring linked Item should be one", 1, scoringRule.getLinkedItems().size());
// save the scoring once again
segmentService.setScoringDefinition(scoring);
refreshPersistence();
// Check linkedItems
rules = persistenceService.getAllItems(Rule.class);
scoringRule = rules.stream().filter(rule -> rule.getItemId().equals(pastEventCondition.getParameter("generatedPropertyKey")))
.findFirst().get();
Assert.assertEquals("Scoring linked Item should be one", 1, scoringRule.getLinkedItems().size());
// Remove scoring
segmentService.removeSegmentDefinition(scoring.getItemId(), true);
refreshPersistence();
// Check linkedItems
rules = persistenceService.getAllItems(Rule.class);
boolean isRule = rules.stream().anyMatch(rule -> rule.getItemId().equals(pastEventCondition.getParameter("generatedPropertyKey")));
Assert.assertFalse("Rule is properly removed", isRule);
}
@Test
public void testSegmentWithRelativeDateExpressions() throws Exception {
// create Profile
Profile profile = new Profile();
profile.setItemId("test_profile_id");
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// create the conditions
Condition booleanCondition = new Condition(definitionsService.getConditionType("booleanCondition"));
List<Condition> subConditions = new ArrayList<>();
Condition dateExpCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition"));
dateExpCondition.setParameter("propertyName", "properties.lastVisit");
dateExpCondition.setParameter("comparisonOperator", "greaterThanOrEqualTo");
dateExpCondition.setParameter("propertyValueDateExpr", "now-5d");
subConditions.add(dateExpCondition);
Condition otherCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition"));
otherCondition.setParameter("propertyName", "properties.address");
otherCondition.setParameter("comparisonOperator", "notEquals");
otherCondition.setParameter("propertyValueDateExpr", "test");
subConditions.add(otherCondition);
booleanCondition.setParameter("operator", "and");
booleanCondition.setParameter("subConditions", subConditions);
// create segment and scoring
Metadata segmentMetadata = new Metadata("relative-date-segment-test");
Segment segment = new Segment(segmentMetadata);
segment.setCondition(booleanCondition);
segmentService.setSegmentDefinition(segment);
Metadata scoringMetadata = new Metadata("relative-date-scoring-test");
Scoring scoring = new Scoring(scoringMetadata);
ScoringElement scoringElement = new ScoringElement();
scoringElement.setCondition(booleanCondition);
scoringElement.setValue(5);
scoring.setElements(Collections.singletonList(scoringElement));
segmentService.setScoringDefinition(scoring);
Thread.sleep(5000);
// insure the profile is not yet engaged since we directly saved the profile in ES
profile = profileService.load("test_profile_id");
Assert.assertFalse("Profile should not be engaged in the segment", profile.getSegments().contains("relative-date-segment-test"));
Assert.assertTrue("Profile should not be engaged in the scoring",
profile.getScores() == null || !profile.getScores().containsKey("relative-date-scoring-test"));
// Update the profile last visit to match the segment ans the scoring
ZoneId defaultZoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now().minusDays(3);
profile.setProperty("lastVisit", Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// insure the profile is not yet engaged since we directly saved the profile in ES
profile = profileService.load("test_profile_id");
Assert.assertFalse("Profile should not be engaged in the segment", profile.getSegments().contains("relative-date-segment-test"));
Assert.assertTrue("Profile should not be engaged in the scoring",
profile.getScores() == null || profile.getScores().containsKey("relative-date-scoring-test"));
// now force the recalculation of the date relative segments/scorings
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should be engaged in the segment and scoring", () -> profileService.load("test_profile_id"),
updatedProfile -> updatedProfile.getSegments().contains("relative-date-segment-test") && updatedProfile.getScores() != null
&& updatedProfile.getScores().get("relative-date-scoring-test") == 5, 1000, 20);
// update the profile to a date out of date expression
localDate = LocalDate.now().minusDays(15);
profile.setProperty("lastVisit", Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
profileService.save(profile);
persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
// now force the recalculation of the date relative segments/scorings
segmentService.recalculatePastEventConditions();
persistenceService.refreshIndex(Profile.class, null);
keepTrying("Profile should not be engaged in the segment and scoring anymore", () -> profileService.load("test_profile_id"),
updatedProfile -> !updatedProfile.getSegments().contains("relative-date-segment-test") && (
updatedProfile.getScores() == null || !updatedProfile.getScores().containsKey("relative-date-scoring-test")), 1000,
20);
}
}