This is an automated email from the ASF dual-hosted git repository.

shuber pushed a commit to branch unomi-3-dev
in repository https://gitbox.apache.org/repos/asf/unomi.git


The following commit(s) were added to refs/heads/unomi-3-dev by this push:
     new 2bc649ce6 UNOMI-925: Fix getPersonaSessions to use PersonaSession 
instead of Session
2bc649ce6 is described below

commit 2bc649ce6f25268706029972a5b31a3bd5573f76
Author: Serge Huber <[email protected]>
AuthorDate: Mon Jan 5 14:32:14 2026 +0100

    UNOMI-925: Fix getPersonaSessions to use PersonaSession instead of Session
    
    The getPersonaSessions method was incorrectly querying for Session objects
    instead of PersonaSession objects, causing it to fail to retrieve persona
    sessions even though they existed and were properly linked to personas.
    
    Changes:
    - Updated ProfileService.getPersonaSessions() to return 
PartialList<PersonaSession>
      instead of PartialList<Session>
    - Fixed ProfileServiceImpl.getPersonaSessions() to query using 
PersonaSession.class
      instead of Session.class in the persistence layer
    - Updated ProfileServiceEndPoint.getPersonaSessions() REST endpoint to 
return
      the correct type
    - Ensured savePersonaWithSessions properly links sessions to personas via
      session.setProfile() to maintain data integrity
    - Added PersonaIT integration test to verify the fix and prevent regressions
    - Added persona-with-sessions-payload.json test resource
    - Added PersonaIT to AllITs test suite
    
    The fix ensures that:
    1. getPersonaSessions correctly queries the PersonaSession collection
    2. Sessions are properly linked to personas when saved
    3. The REST endpoint GET /cxs/profiles/personas/{personaId}/sessions works 
correctly
    4. Integration tests verify the functionality and prevent future regressions
    
    This resolves the issue where persona sessions could not be retrieved via
    getPersonaSessions even though they were successfully saved and could be
    retrieved via loadPersonaWithSessions.
    
    No compatibility concerns as this method/endpoint was not in use prior to
    this fix (no issues were reported).
---
 .../apache/unomi/api/services/ProfileService.java  |   2 +-
 .../test/java/org/apache/unomi/itests/AllITs.java  |   1 +
 .../java/org/apache/unomi/itests/PersonaIT.java    | 154 +++++++++++++++++++++
 .../persona/persona-with-sessions-payload.json     |  36 +++++
 .../rest/endpoints/ProfileServiceEndPoint.java     |   2 +-
 .../services/impl/profiles/ProfileServiceImpl.java |   4 +-
 6 files changed, 195 insertions(+), 4 deletions(-)

diff --git 
a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java 
b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
index 7da4d35b6..566e6e427 100644
--- a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java
@@ -278,7 +278,7 @@ public interface ProfileService {
      *                  a column ({@code :}) and an order specifier: {@code 
asc} or {@code desc}.
      * @return a {@link PartialList} of sessions for the persona identified by 
the specified identifier
      */
-    PartialList<Session> getPersonaSessions(String personaId, int offset, int 
size, String sortBy);
+    PartialList<PersonaSession> getPersonaSessions(String personaId, int 
offset, int size, String sortBy);
 
     /**
      * Save a persona with its sessions.
diff --git a/itests/src/test/java/org/apache/unomi/itests/AllITs.java 
b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
index e9ec8ab60..659a13e03 100644
--- a/itests/src/test/java/org/apache/unomi/itests/AllITs.java
+++ b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
@@ -37,6 +37,7 @@ import org.junit.runners.Suite.SuiteClasses;
         ConditionQueryBuilderIT.class,
         SegmentIT.class,
         ProfileServiceIT.class,
+        PersonaIT.class,
         ProfileImportBasicIT.class,
         ProfileImportSurfersIT.class,
         ProfileImportRankingIT.class,
diff --git a/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java 
b/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java
new file mode 100644
index 000000000..4e65072d2
--- /dev/null
+++ b/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.fasterxml.jackson.core.type.TypeReference;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.PersonaSession;
+import org.apache.unomi.api.PersonaWithSessions;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+/**
+ * Integration tests for persona functionality.
+ * This test class covers persona-related features including persona sessions.
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerSuite.class)
+public class PersonaIT extends BaseIT {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(PersonaIT.class);
+    
+    private static final String BASE_PROFILES_PATH = "/cxs/profiles";
+    private static final String PERSONA_WITH_SESSIONS_ENDPOINT = 
BASE_PROFILES_PATH + "/personasWithSessions";
+    private static final String PERSONA_ENDPOINT = BASE_PROFILES_PATH + 
"/personas";
+    private static final String PERSONA_BY_ID_ENDPOINT = PERSONA_ENDPOINT + 
"/{personaId}";
+    private static final String PERSONA_SESSIONS_ENDPOINT = PERSONA_ENDPOINT + 
"/{personaId}/sessions";
+    
+    private static final String TEST_PERSONA_ID = "test-persona-with-sessions";
+    private static final String TEST_SESSION_ID = "test-session-1";
+    private static final String PAYLOAD_RESOURCE = 
"persona/persona-with-sessions-payload.json";
+
+    @Before
+    public void setUp() throws InterruptedException {
+        // Wait for persona REST endpoint to be available
+        // Using GET /personas/{personaId} with a dummy ID to check endpoint 
availability
+        String checkEndpoint = PERSONA_BY_ID_ENDPOINT.replace("{personaId}", 
"endpoint-check");
+        keepTrying("Couldn't find persona endpoint", () -> {
+            try (CloseableHttpResponse response = executeHttpRequest(new 
HttpGet(getFullUrl(checkEndpoint)), AuthType.JAAS_ADMIN)) {
+                // Endpoint exists if we get 200 (persona exists), 204 (no 
content - persona not found), or 404 (not found)
+                int statusCode = response.getStatusLine().getStatusCode();
+                return (statusCode == 200 || statusCode == 204 || statusCode 
== 404) ? response : null;
+            } catch (Exception e) {
+                return null;
+            }
+        }, Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+    }
+
+    @After
+    public void tearDown() {
+        // Clean up: delete the test persona
+        try {
+            profileService.delete(TEST_PERSONA_ID, true);
+        } catch (Exception e) {
+            LOGGER.warn("Failed to clean up test persona: {}", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testSavePersonaWithSessionsAndRetrieveSessions() throws 
Exception {
+        // Create persona with sessions via REST API
+        HttpPost createRequest = new 
HttpPost(getFullUrl(PERSONA_WITH_SESSIONS_ENDPOINT));
+        createRequest.setEntity(new 
StringEntity(resourceAsString(PAYLOAD_RESOURCE), JSON_CONTENT_TYPE));
+
+        PersonaWithSessions createdPersona;
+        try (CloseableHttpResponse createResponse = 
executeHttpRequest(createRequest, AuthType.JAAS_ADMIN)) {
+            int statusCode = createResponse.getStatusLine().getStatusCode();
+            Assert.assertEquals("Persona creation should return 200 OK", 200, 
statusCode);
+
+            String responseBody = 
EntityUtils.toString(createResponse.getEntity());
+            createdPersona = 
CustomObjectMapper.getObjectMapper().readValue(responseBody, 
PersonaWithSessions.class);
+        }
+
+        Assert.assertNotNull("Created persona should not be null", 
createdPersona);
+        Assert.assertNotNull("Created persona should have persona object", 
createdPersona.getPersona());
+        Assert.assertEquals("Persona ID should match", TEST_PERSONA_ID, 
createdPersona.getPersona().getItemId());
+        Assert.assertNotNull("Created persona should have sessions", 
createdPersona.getSessions());
+        Assert.assertFalse("Created persona should have at least one session", 
createdPersona.getSessions().isEmpty());
+
+        // Wait for persona's sessions to be indexed before testing session 
retrieval
+        // This ensures the sessions are properly linked and queryable
+        String sessionsUrl = PERSONA_SESSIONS_ENDPOINT.replace("{personaId}", 
TEST_PERSONA_ID);
+        PartialList<PersonaSession> sessions = keepTrying(
+            "Persona sessions should be retrievable after creation",
+            () -> {
+                try {
+                    try (CloseableHttpResponse response = 
executeHttpRequest(new HttpGet(getFullUrl(sessionsUrl)), AuthType.JAAS_ADMIN)) {
+                        if (response.getStatusLine().getStatusCode() == 200) {
+                            String responseBody = 
EntityUtils.toString(response.getEntity());
+                            PartialList<PersonaSession> result = 
CustomObjectMapper.getObjectMapper().readValue(
+                                responseBody, new 
TypeReference<PartialList<PersonaSession>>() {});
+                            // Check if the test session is present
+                            if (result != null && result.getList() != null && 
!result.getList().isEmpty()) {
+                                boolean hasTestSession = 
result.getList().stream()
+                                    .anyMatch(session -> 
TEST_SESSION_ID.equals(session.getItemId()));
+                                return hasTestSession ? result : null;
+                            }
+                        }
+                        return null;
+                    }
+                } catch (Exception e) {
+                    LOGGER.debug("Error retrieving persona sessions: {}", 
e.getMessage());
+                    return null;
+                }
+            },
+            Objects::nonNull,
+            DEFAULT_TRYING_TIMEOUT,
+            DEFAULT_TRYING_TRIES * 2 // Give more time for indexing
+        );
+
+        Assert.assertNotNull("Persona sessions should be retrievable", 
sessions);
+        Assert.assertNotNull("Sessions list should not be null", 
sessions.getList());
+        Assert.assertFalse("Sessions list should not be empty", 
sessions.getList().isEmpty());
+
+        // Verify the test session is present and properly linked
+        PersonaSession testSession = sessions.getList().stream()
+            .filter(session -> TEST_SESSION_ID.equals(session.getItemId()))
+            .findFirst()
+            .orElse(null);
+
+        Assert.assertNotNull("Test session should be found in retrieved 
sessions", testSession);
+        Assert.assertNotNull("Session should have a profile reference", 
testSession.getProfile());
+        Assert.assertEquals("Session should be linked to the correct persona", 
TEST_PERSONA_ID, testSession.getProfile().getItemId());
+    }
+}
+
diff --git 
a/itests/src/test/resources/persona/persona-with-sessions-payload.json 
b/itests/src/test/resources/persona/persona-with-sessions-payload.json
new file mode 100644
index 000000000..15944f46f
--- /dev/null
+++ b/itests/src/test/resources/persona/persona-with-sessions-payload.json
@@ -0,0 +1,36 @@
+{
+  "persona": {
+    "itemId": "test-persona-with-sessions",
+    "version": null,
+    "properties": {
+      "firstName": "Test",
+      "lastName": "Persona",
+      "email": "[email protected]"
+    },
+    "systemProperties": {},
+    "segments": [],
+    "scores": {},
+    "mergedWith": null,
+    "consents": {}
+  },
+  "sessions": [
+    {
+      "itemId": "test-session-1",
+      "scope": "test",
+      "profile": {
+        "itemId": "test-persona-with-sessions",
+        "properties": {
+          "firstName": "Test",
+          "lastName": "Persona"
+        }
+      },
+      "properties": {
+        "operatingSystemName": "OS X",
+        "sessionStartDate": "2024-01-01T00:00:00Z"
+      },
+      "timeStamp": "2024-01-01T00:00:00Z",
+      "lastEventDate": "2024-01-01T01:00:00Z"
+    }
+  ]
+}
+
diff --git 
a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
 
b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
index d1ed6d67b..081386dee 100644
--- 
a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
+++ 
b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
@@ -392,7 +392,7 @@ public class ProfileServiceEndPoint {
      */
     @GET
     @Path("/personas/{personaId}/sessions")
-    public PartialList<Session> getPersonaSessions(@PathParam("personaId") 
String personaId,
+    public PartialList<PersonaSession> 
getPersonaSessions(@PathParam("personaId") String personaId,
                                                    @QueryParam("offset") 
@DefaultValue("0") int offset,
                                                    @QueryParam("size") 
@DefaultValue("50") int size,
                                                    @QueryParam("sort") String 
sortBy) {
diff --git 
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
 
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
index b0c355cd6..61b077375 100644
--- 
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
+++ 
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
@@ -945,8 +945,8 @@ public class ProfileServiceImpl extends 
AbstractMultiTypeCachingService implemen
         return getItem(id, PropertyType.class);
     }
 
-    public PartialList<Session> getPersonaSessions(String personaId, int 
offset, int size, String sortBy) {
-        return persistenceService.query("profileId", personaId, sortBy, 
Session.class, offset, size);
+    public PartialList<PersonaSession> getPersonaSessions(String personaId, 
int offset, int size, String sortBy) {
+        return persistenceService.query("profileId", personaId, sortBy, 
PersonaSession.class, offset, size);
     }
 
     public PersonaWithSessions savePersonaWithSessions(PersonaWithSessions 
personaToSave) {

Reply via email to