This is an automated email from the ASF dual-hosted git repository. jkevan pushed a commit to branch UNOMI-727-adapt-merge-rollover in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 2d456a67fde9401d7c4a881acff1604d2be857ae Author: Kevan <ke...@jahia.com> AuthorDate: Thu Mar 9 19:28:01 2023 +0100 UNOMI-727: adapt merge system to rollover (and cleanup too) --- .../actions/MergeProfilesOnPropertyAction.java | 269 ++++++++++----------- 1 file changed, 131 insertions(+), 138 deletions(-) diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java index 2261a2a4b..49d9c7cc7 100644 --- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java +++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java @@ -24,14 +24,15 @@ import org.apache.unomi.api.Profile; import org.apache.unomi.api.Session; import org.apache.unomi.api.actions.Action; import org.apache.unomi.api.actions.ActionExecutor; -import org.apache.unomi.api.actions.ActionPostExecutor; import org.apache.unomi.api.conditions.Condition; import org.apache.unomi.api.services.*; import org.apache.unomi.persistence.spi.PersistenceService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; public class MergeProfilesOnPropertyAction implements ActionExecutor { @@ -46,171 +47,163 @@ public class MergeProfilesOnPropertyAction implements ActionExecutor { public int execute(Action action, Event event) { - Profile profile = event.getProfile(); - if (profile instanceof Persona || profile.isAnonymousProfile()) { + Profile eventProfile = event.getProfile(); + final String mergePropName = (String) action.getParameterValues().get("mergeProfilePropertyName"); + final String mergePropValue = (String) action.getParameterValues().get("mergeProfilePropertyValue"); + boolean forceEventProfileAsMaster = action.getParameterValues().containsKey("forceEventProfileAsMaster") ? (boolean) action.getParameterValues().get("forceEventProfileAsMaster") : false; + final String currentProfileMergeValue = (String) eventProfile.getSystemProperties().get(mergePropName); + + if (eventProfile instanceof Persona || eventProfile.isAnonymousProfile() || StringUtils.isEmpty(mergePropName) || + StringUtils.isEmpty(mergePropValue)) { return EventService.NO_CHANGE; } - final String mergeProfilePropertyName = (String) action.getParameterValues().get("mergeProfilePropertyName"); - if (StringUtils.isEmpty(mergeProfilePropertyName)) { - return EventService.NO_CHANGE; + final List<Profile> profilesToBeMerge = getProfilesToBeMerge(mergePropName, mergePropValue); + + // Check if the user switched to another profile + if (StringUtils.isNotEmpty(currentProfileMergeValue) && !currentProfileMergeValue.equals(mergePropValue)) { + reassignSession(event, profilesToBeMerge, forceEventProfileAsMaster, mergePropName, mergePropValue); + return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED; } - final String mergeProfilePropertyValue = (String) action.getParameterValues().get("mergeProfilePropertyValue"); - if (StringUtils.isEmpty(mergeProfilePropertyValue)) { - return EventService.NO_CHANGE; + // Store merge prop on current profile + boolean profileUpdated = false; + if (StringUtils.isEmpty(currentProfileMergeValue)) { + profileUpdated = true; + eventProfile.getSystemProperties().put(mergePropName, mergePropValue); } - final String mergeProfilePreviousPropertyValue = profile.getSystemProperties().get(mergeProfilePropertyName) != null ? profile.getSystemProperties().get(mergeProfilePropertyName).toString() : ""; + // If not profiles to merge we are done here. + if (profilesToBeMerge.isEmpty()) { + return profileUpdated ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE; + } - final Session currentSession = event.getSession(); + // add current Profile to profiles to be merged + if (profilesToBeMerge.stream().noneMatch(p -> StringUtils.equals(p.getItemId(), eventProfile.getItemId()))) { + profilesToBeMerge.add(eventProfile); + } - boolean forceEventProfileAsMaster = action.getParameterValues().containsKey("forceEventProfileAsMaster") ? - (boolean) action.getParameterValues().get("forceEventProfileAsMaster") : false; + final String eventProfileId = eventProfile.getItemId(); + final Profile mergedProfile = profileService.mergeProfiles(forceEventProfileAsMaster ? eventProfile : profilesToBeMerge.get(0), profilesToBeMerge); + final String mergedProfileId = mergedProfile.getItemId(); - // store the profile id in case the merge change it to a previous one - String profileId = profile.getItemId(); + // Profile is still using the same profileId after being merged, no need to rewrite exists data, merge is done + if (!forceEventProfileAsMaster && mergedProfileId.equals(eventProfileId)) { + return profileUpdated ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE; + } - Condition propertyCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition")); - propertyCondition.setParameter("comparisonOperator", "equals"); - propertyCondition.setParameter("propertyName", "systemProperties." + mergeProfilePropertyName); - propertyCondition.setParameter("propertyValue", mergeProfilePropertyValue); + // ProfileID changed we have a lot to do + // First check for privacy stuff (inherit from previous profile if necessary) + if (privacyService.isRequireAnonymousBrowsing(eventProfile)) { + privacyService.setRequireAnonymousBrowsing(mergedProfileId, true, event.getScope()); + } + final boolean anonymousBrowsing = privacyService.isRequireAnonymousBrowsing(mergedProfileId); - final List<Profile> profiles = persistenceService.query(propertyCondition, "properties.firstVisit", Profile.class, 0, maxProfilesInOneMerge).getList(); + // Modify current session: + if (event.getSession() != null) { + event.getSession().setProfile(anonymousBrowsing ? privacyService.getAnonymousProfile(mergedProfile) : mergedProfile); + } - // Check if the user switched to another profile - if (StringUtils.isNotEmpty(mergeProfilePreviousPropertyValue) && !mergeProfilePreviousPropertyValue.equals(mergeProfilePropertyValue)) { - if (profiles.size() > 0) { - // Take existing profile - profile = profiles.get(0); - } else { - if (forceEventProfileAsMaster) { - profile = event.getProfile(); - } else { - // Create a new profile - profile = new Profile(UUID.randomUUID().toString()); - profile.setProperty("firstVisit", event.getTimeStamp()); + // Modify current event: + event.setProfileId(anonymousBrowsing ? null : mergedProfileId); + event.setProfile(mergedProfile); + + event.getActionPostExecutors().add(() -> { + try { + // Save event, as we changed the profileId of the current event + if (event.isPersistent()) { + persistenceService.save(event); } - profile.getSystemProperties().put(mergeProfilePropertyName, mergeProfilePropertyValue); - } - logger.info("Different users, switch to " + profile.getItemId()); - // At the end of the merge, we must set the merged profile as profile event to process other Actions - event.setProfileId(profile.getItemId()); - event.setProfile(profile); + for (Profile profileToBeMerge : profilesToBeMerge) { + String profileToBeMergeId = profileToBeMerge.getItemId(); + if (!StringUtils.equals(profileToBeMergeId, mergedProfileId)) { - if (currentSession != null) { - currentSession.setProfile(profile); - eventService.send(new Event("sessionReassigned", currentSession, profile, event.getScope(), event, currentSession, - null, event.getTimeStamp(), false)); - } + // todo move in update by query + script + List<Session> oldSessions = persistenceService.query("profileId", profileToBeMergeId, null, Session.class); + for (Session oldSession : oldSessions) { + if (!oldSession.getItemId().equals(event.getSession().getItemId())) { + persistenceService.update(oldSession, Session.class, "profileId", anonymousBrowsing ? null : mergedProfileId); + } + } - return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED; - } else { - // Store the merge property identifier in the profile - profile.getSystemProperties().put(mergeProfilePropertyName, mergeProfilePropertyValue); + // todo move in update by query + script + List<Event> oldEvents = persistenceService.query("profileId", profileToBeMergeId, null, Event.class); + for (Event oldEvent : oldEvents) { + if (!oldEvent.getItemId().equals(event.getItemId())) { + persistenceService.update(oldEvent, Event.class, "profileId", anonymousBrowsing ? null : mergedProfileId); + } + } - // add current Profile to profiles to be merged - boolean add = true; - for (Profile p : profiles) { - add = add && !StringUtils.equals(p.getItemId(), profile.getItemId()); - } - if (add) { - profiles.add(profile); + final String clientIdFromEvent = (String) event.getAttributes().get(Event.CLIENT_ID_ATTRIBUTE); + String clientId = clientIdFromEvent != null ? clientIdFromEvent : "defaultClientId"; + profileService.addAliasToProfile(mergedProfileId, profileToBeMergeId, clientId); + if (profileService.load(profileToBeMergeId) != null) { + profileService.delete(profileToBeMergeId, false); + } + } + } + } catch (Exception e) { + logger.error("unable to execute callback action, profile and session will not be saved", e); + return false; } + return true; + }); - if (profiles.size() == 1) { - return StringUtils.isEmpty(mergeProfilePreviousPropertyValue) ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE; - } + return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED; + } - Profile markedMasterProfile; - if (forceEventProfileAsMaster) - markedMasterProfile = event.getProfile(); - else - markedMasterProfile = profiles.get(0);// Use oldest profile for master profile + private List<Profile> getProfilesToBeMerge(String mergeProfilePropertyName, String mergeProfilePropertyValue) { + Condition propertyCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + propertyCondition.setParameter("comparisonOperator", "equals"); + propertyCondition.setParameter("propertyName", "systemProperties." + mergeProfilePropertyName); + propertyCondition.setParameter("propertyValue", mergeProfilePropertyValue); - final Profile masterProfile = profileService.mergeProfiles(markedMasterProfile, profiles); + return persistenceService.query(propertyCondition, "properties.firstVisit", Profile.class, 0, maxProfilesInOneMerge).getList(); + } - // Profile has changed - if (forceEventProfileAsMaster || !masterProfile.getItemId().equals(profileId)) { + private void reassignSession(Event event, List<Profile> existingMergedProfiles, boolean forceEventProfileAsMaster, String mergePropName, String mergePropValue) { + Profile eventProfile = event.getProfile(); - final String masterProfileId = masterProfile.getItemId(); - // At the end of the merge, we must set the merged profile as profile event to process other Actions - event.setProfileId(masterProfileId); - event.setProfile(masterProfile); + if (existingMergedProfiles.size() > 0) { + // Take existing profile + eventProfile = existingMergedProfiles.get(0); + } else { + if (!forceEventProfileAsMaster) { + // Create a new profile + eventProfile = new Profile(UUID.randomUUID().toString()); + eventProfile.setProperty("firstVisit", event.getTimeStamp()); + } + eventProfile.getSystemProperties().put(mergePropName, mergePropValue); + } - final Boolean anonymousBrowsing = privacyService.isRequireAnonymousBrowsing(masterProfileId); + logger.info("Different users, switch to " + eventProfile.getItemId()); + // At the end of the merge, we must set the merged profile as profile event to process other Actions + event.setProfileId(eventProfile.getItemId()); + event.setProfile(eventProfile); - if (currentSession != null) { - currentSession.setProfile(masterProfile); - if (privacyService.isRequireAnonymousBrowsing(profile)) { - privacyService.setRequireAnonymousBrowsing(masterProfileId, true, event.getScope()); - } + if (event.getSession() != null) { + Session eventSession = event.getSession(); + eventSession.setProfile(eventProfile); + eventService.send(new Event("sessionReassigned", eventSession, eventProfile, event.getScope(), event, eventSession, + null, event.getTimeStamp(), false)); + } + } - if (anonymousBrowsing) { - currentSession.setProfile(privacyService.getAnonymousProfile(masterProfile)); - event.setProfileId(null); - persistenceService.save(event); - } - } + private void updateAllSessionsForProfile(String newProfileId, String oldProfileId) { + String[] scripts = new String[]{"updateProfileId"}; - event.getActionPostExecutors().add(new ActionPostExecutor() { - @Override - public boolean execute() { - try { - Event currentEvent = event; - // Update current event explicitly, as it might not return from search query if there wasn't a refresh in ES - if (!StringUtils.equals(profileId, masterProfileId)) { - if (currentEvent.isPersistent()) { - persistenceService.update(currentEvent, Event.class, "profileId", anonymousBrowsing ? null : masterProfileId); - } - } + Map<String, Object>[] scriptParams = new HashMap[1]; + scriptParams[0] = new HashMap<>(); + scriptParams[0].put("profileId", newProfileId); - for (Profile profile : profiles) { - String profileId = profile.getItemId(); - if (!StringUtils.equals(profileId, masterProfileId)) { - // TODO consider udpate by query and/or script - List<Session> sessions = persistenceService.query("profileId", profileId, null, Session.class); - if (currentSession != null) { - if (masterProfileId.equals(profileId) && !sessions.contains(currentSession)) { - sessions.add(currentSession); - } - } - - for (Session session : sessions) { - persistenceService.update(session, Session.class, "profileId", anonymousBrowsing ? null : masterProfileId); - } - - // TODO consider udpate by query and/or script - List<Event> events = persistenceService.query("profileId", profileId, null, Event.class); - for (Event event : events) { - if (!event.getItemId().equals(currentEvent.getItemId())) { - persistenceService.update(event, Event.class, "profileId", anonymousBrowsing ? null : masterProfileId); - } - } - - final String clientIdFromEvent = (String) event.getAttributes().get(Event.CLIENT_ID_ATTRIBUTE); - String clientId = clientIdFromEvent != null ? clientIdFromEvent : "defaultClientId"; - profileService.addAliasToProfile(masterProfileId, profile.getItemId(), clientId); - - boolean isExist = profileService.load(profile.getItemId()) != null; - if (isExist) { - profileService.delete(profileId, false); - } - } - } - } catch (Exception e) { - logger.error("unable to execute callback action, profile and session will not be saved", e); - return false; - } - return true; - } - }); - return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED; - } else { - return StringUtils.isEmpty(mergeProfilePreviousPropertyValue) ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE; - } - } + Condition[] conditions = new Condition[1]; + conditions[0] = new Condition(); + conditions[0].setConditionType(definitionsService.getConditionType("sessionPropertyCondition")); + conditions[0].setParameter("propertyName", "profileId"); + conditions[0].setParameter("comparisonOperator", "equals"); + conditions[0].setParameter("propertyValue", oldProfileId); + persistenceService.updateWithQueryAndStoredScript(Session.class, scripts, scriptParams, conditions); } public void setProfileService(ProfileService profileService) { @@ -236,4 +229,4 @@ public class MergeProfilesOnPropertyAction implements ActionExecutor { public void setMaxProfilesInOneMerge(String maxProfilesInOneMerge) { this.maxProfilesInOneMerge = Integer.parseInt(maxProfilesInOneMerge); } -} +} \ No newline at end of file