From 164fb9ee54594338715568d8a7234effa72ec9ad Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Thu, 28 Aug 2025 17:18:07 +0200 Subject: [PATCH 1/7] [SYNCOPE-1906] Fixing order by clause on plain attribute while using non jsonb persistence --- .../persistence/jpa/dao/JPAAnySearchDAO.java | 9 +- .../persistence/jpa/inner/AnySearchTest.java | 97 +++++++++++++++++++ .../syncope/fit/core/UserIssuesITCase.java | 31 ++++++ 3 files changed, 136 insertions(+), 1 deletion(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 1c11721cbbc..5b25355fae5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -1006,7 +1006,14 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.asSearchViewSupport().attr()); item.select = new StringBuilder(). - append(svs.asSearchViewSupport().attr().alias).append('.').append(key(schema.getType())). + append("( SELECT usa").append('.').append(key(schema.getType())). + append(" FROM "). + append(svs.asSearchViewSupport().attr().name). + append(" usa WHERE usa.any_id = "). + append(svs.asSearchViewSupport().attr().alias). + append(".any_id"). + append(" AND usa.schema_id ='").append(fieldName).append("'"). + append(" LIMIT 1 )"). append(" AS ").append(fieldName).toString(); item.where = new StringBuilder(). append(svs.asSearchViewSupport().attr().alias). diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index 5218d666972..819e80fc41b 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -34,12 +34,17 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.rest.api.beans.AnyQuery; +import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; @@ -55,7 +60,11 @@ import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; import org.apache.syncope.core.persistence.api.dao.search.RoleCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; @@ -100,6 +109,15 @@ public class AnySearchTest extends AbstractTest { @Autowired private RoleDAO roleDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private EntityFactory entityFactory; + + @Autowired + private PlainAttrValidationManager validator; @BeforeEach public void adjustLoginDateForLocalSystem() throws ParseException { @@ -973,4 +991,83 @@ public void issueSYNCOPE1419() { assertNotNull(users); assertEquals(4, users.size()); } + + @Test + public void issueSYNCOPE1906() { + User bellini = userDAO.findByUsername("bellini"); + assertNotNull(bellini); + + PlainSchema ctypeSchema = plainSchemaDAO.find("ctype"); + assertNotNull(ctypeSchema); + + userDAO.save(addPlainAttr(bellini, ctypeSchema, "aa1")); + + User puccini = userDAO.findByUsername("puccini"); + assertNotNull(bellini); + + userDAO.save(addPlainAttr(puccini, ctypeSchema, "aa2")); + + User verdi = userDAO.findByUsername("verdi"); + assertNotNull(verdi); + + userDAO.save(addPlainAttr(verdi, ctypeSchema, "aa3")); + + User vivaldi = userDAO.findByUsername("vivaldi"); + assertNotNull(vivaldi); + + userDAO.save(addPlainAttr(vivaldi, ctypeSchema, "aa4")); + + User rossini = userDAO.findByUsername("rossini"); + assertNotNull(rossini); + + userDAO.save(addPlainAttr(rossini, ctypeSchema, "aa5")); + + entityManager().flush(); + + OrderByClause orderByCtype = new OrderByClause(); + orderByCtype.setField("ctype"); + orderByCtype.setDirection(OrderByClause.Direction.DESC); + + OrderByClause orderByFirstname = new OrderByClause(); + orderByFirstname.setField("username"); + orderByFirstname.setDirection(OrderByClause.Direction.DESC); + + AnyCond idCond = new AnyCond(AttrCond.Type.ISNOTNULL); + idCond.setSchema("id"); + + List users = + searchDAO.search(SearchCond.getLeaf(idCond), List.of(orderByCtype, orderByFirstname), AnyTypeKind.USER); + + assertEquals("bellini", users.get(4).getUsername()); + assertEquals("puccini", users.get(3).getUsername()); + assertEquals("verdi", users.get(2).getUsername()); + assertEquals("vivaldi", users.get(1).getUsername()); + assertEquals("rossini", users.get(0).getUsername()); + + AttrCond surnameCond = new AttrCond(AttrCond.Type.IEQ); + surnameCond.setSchema("surname"); + surnameCond.setExpression("%ini"); + + AttrCond coolCond = new AttrCond(AttrCond.Type.ISNULL); + coolCond.setSchema("cool"); + + users = searchDAO.search(SearchCond.getAnd(SearchCond.getLeaf(surnameCond), SearchCond.getLeaf(coolCond)), + List.of(orderByCtype), AnyTypeKind.USER); + + assertFalse(users.isEmpty()); + + } + + private User addPlainAttr(final User user, final PlainSchema plainSchema, final String value) { + user.getPlainAttr(plainSchema.getKey()) + .ifPresentOrElse(ctype -> ctype.getValues().get(0).setStringValue(value), () -> { + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(plainSchema); + attr.add(validator, value, anyUtilsFactory.getInstance(AnyTypeKind.USER)); + + user.add(attr); + }); + return user; + } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index ea9fc0a6a19..88d1ac5eb3d 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -104,6 +104,7 @@ import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.UnmatchingRule; import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.beans.AnyQuery; import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.syncope.common.rest.api.beans.ReconQuery; import org.apache.syncope.common.rest.api.beans.TaskQuery; @@ -1923,4 +1924,34 @@ public void issueSYNCOPE1894() { SCHEMA_SERVICE.delete(SchemaType.PLAIN, userWithDotSchema.getKey()); } } + + @Test + public void issueSYNCOPE1906() { + + updateUser( + new UserUR.Builder(USER_SERVICE.read("bellini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa1")) + .build()); + updateUser( + new UserUR.Builder(USER_SERVICE.read("puccini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa2")) + .build()); + updateUser( + new UserUR.Builder(USER_SERVICE.read("verdi").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa3")) + .build()); + updateUser( + new UserUR.Builder(USER_SERVICE.read("vivaldi").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa4")) + .build()); + updateUser( + new UserUR.Builder(USER_SERVICE.read("rossini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa5")) + .build()); + + List users = + USER_SERVICE.search(new AnyQuery.Builder().size(10).page(1) + .orderBy("ctype DESC").build()).getResult(); + + assertEquals("bellini", users.get(4).getUsername()); + assertEquals("puccini", users.get(3).getUsername()); + assertEquals("verdi", users.get(2).getUsername()); + assertEquals("vivaldi", users.get(1).getUsername()); + assertEquals("rossini", users.get(0).getUsername()); + } } From 4312f18d48e11787ca7bc2a3820acbe67b1a238e Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Thu, 28 Aug 2025 17:26:01 +0200 Subject: [PATCH 2/7] fix checkstyle --- .../persistence/jpa/inner/AnySearchTest.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index 819e80fc41b..b51b7596995 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -34,11 +34,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.request.UserUR; -import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.apache.syncope.common.rest.api.beans.AnyQuery; import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; @@ -60,10 +57,8 @@ import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; import org.apache.syncope.core.persistence.api.dao.search.RoleCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; -import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; @@ -1011,10 +1006,10 @@ public void issueSYNCOPE1906() { assertNotNull(verdi); userDAO.save(addPlainAttr(verdi, ctypeSchema, "aa3")); - + User vivaldi = userDAO.findByUsername("vivaldi"); assertNotNull(vivaldi); - + userDAO.save(addPlainAttr(vivaldi, ctypeSchema, "aa4")); User rossini = userDAO.findByUsername("rossini"); @@ -1023,7 +1018,7 @@ public void issueSYNCOPE1906() { userDAO.save(addPlainAttr(rossini, ctypeSchema, "aa5")); entityManager().flush(); - + OrderByClause orderByCtype = new OrderByClause(); orderByCtype.setField("ctype"); orderByCtype.setDirection(OrderByClause.Direction.DESC); @@ -1047,15 +1042,14 @@ public void issueSYNCOPE1906() { AttrCond surnameCond = new AttrCond(AttrCond.Type.IEQ); surnameCond.setSchema("surname"); surnameCond.setExpression("%ini"); - + AttrCond coolCond = new AttrCond(AttrCond.Type.ISNULL); coolCond.setSchema("cool"); users = searchDAO.search(SearchCond.getAnd(SearchCond.getLeaf(surnameCond), SearchCond.getLeaf(coolCond)), List.of(orderByCtype), AnyTypeKind.USER); - + assertFalse(users.isEmpty()); - } private User addPlainAttr(final User user, final PlainSchema plainSchema, final String value) { From 38e396d3b8cc29bdd39b36f3d58c5ec3856d7f7d Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Tue, 2 Sep 2025 11:03:50 +0200 Subject: [PATCH 3/7] fixing IT and unit test --- .../persistence/jpa/inner/AnySearchTest.java | 4 +- .../syncope/fit/core/UserIssuesITCase.java | 64 +++++++++++-------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index b51b7596995..5f917efca47 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -1039,7 +1039,8 @@ public void issueSYNCOPE1906() { assertEquals("vivaldi", users.get(1).getUsername()); assertEquals("rossini", users.get(0).getUsername()); - AttrCond surnameCond = new AttrCond(AttrCond.Type.IEQ); + // order by ctype even not searching by it + AttrCond surnameCond = new AttrCond(AttrCond.Type.ILIKE); surnameCond.setSchema("surname"); surnameCond.setExpression("%ini"); @@ -1050,6 +1051,7 @@ public void issueSYNCOPE1906() { List.of(orderByCtype), AnyTypeKind.USER); assertFalse(users.isEmpty()); + assertEquals(2, users.size()); } private User addPlainAttr(final User user, final PlainSchema plainSchema, final String value) { diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index 88d1ac5eb3d..9fd27dff565 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1927,31 +1927,45 @@ public void issueSYNCOPE1894() { @Test public void issueSYNCOPE1906() { + UserCR userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("ctype", "aa1")); + UserTO aa1 = createUser(userCR).getEntity(); - updateUser( - new UserUR.Builder(USER_SERVICE.read("bellini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa1")) - .build()); - updateUser( - new UserUR.Builder(USER_SERVICE.read("puccini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa2")) - .build()); - updateUser( - new UserUR.Builder(USER_SERVICE.read("verdi").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa3")) - .build()); - updateUser( - new UserUR.Builder(USER_SERVICE.read("vivaldi").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa4")) - .build()); - updateUser( - new UserUR.Builder(USER_SERVICE.read("rossini").getKey()).plainAttr(attrAddReplacePatch("ctype", "aa5")) - .build()); - - List users = - USER_SERVICE.search(new AnyQuery.Builder().size(10).page(1) - .orderBy("ctype DESC").build()).getResult(); - - assertEquals("bellini", users.get(4).getUsername()); - assertEquals("puccini", users.get(3).getUsername()); - assertEquals("verdi", users.get(2).getUsername()); - assertEquals("vivaldi", users.get(1).getUsername()); - assertEquals("rossini", users.get(0).getUsername()); + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("ctype", "aa2")); + UserTO aa2 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("ctype", "aa3")); + UserTO aa3 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("ctype", "aa4")); + UserTO aa4 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("ctype", "aa5")); + UserTO aa5 = createUser(userCR).getEntity(); + try { + List users = USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) + .size(10) + .page(1) + .orderBy("ctype DESC") + .build()).getResult(); + + assertEquals(aa1.getUsername(), users.get(4).getUsername()); + assertEquals(aa2.getUsername(), users.get(3).getUsername()); + assertEquals(aa3.getUsername(), users.get(2).getUsername()); + assertEquals(aa4.getUsername(), users.get(1).getUsername()); + assertEquals(aa5.getUsername(), users.get(0).getUsername()); + } finally { + USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) + .size(10) + .page(1) + .orderBy("ctype DESC") + .build()).getResult().forEach(u -> deleteUser(u.getKey())); + } } } From fe8ca1b47c8b51f830d98bd78f55fcbbfb4e9524 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Tue, 2 Sep 2025 16:08:49 +0200 Subject: [PATCH 4/7] fixes oracle and IT test for ES and OS --- .../syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java | 4 ++-- .../java/org/apache/syncope/fit/core/UserIssuesITCase.java | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 5b25355fae5..5ec0e795b76 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -1013,8 +1013,8 @@ protected void parseOrderByForPlainSchema( append(svs.asSearchViewSupport().attr().alias). append(".any_id"). append(" AND usa.schema_id ='").append(fieldName).append("'"). - append(" LIMIT 1 )"). - append(" AS ").append(fieldName).toString(); + append(isOracle() ? " FETCH FIRST 1 ROWS ONLY " : " LIMIT 1"). + append(") AS ").append(fieldName).toString(); item.where = new StringBuilder(). append(svs.asSearchViewSupport().attr().alias). append(".schema_id='").append(fieldName).append("'").toString(); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index 9fd27dff565..d578fe01857 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1947,6 +1947,12 @@ public void issueSYNCOPE1906() { userCR.getPlainAttrs().add(attr("ctype", "aa5")); UserTO aa5 = createUser(userCR).getEntity(); try { + await().until(() -> USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) + .size(0) + .page(1) + .orderBy("ctype DESC") + .build()).getTotalCount() == 5); List users = USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) .size(10) From 99c68ce6ffeb533b42fe47a581a1451e587651da Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Tue, 2 Sep 2025 18:03:55 +0200 Subject: [PATCH 5/7] fixing MyJSON --- .../core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java index ec86a34bddb..b84fe7c4aa0 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java @@ -80,9 +80,12 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.field()); - item.select = svs.field().alias + '.' - + (schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType())) - + " AS " + fieldName; + item.select = + "JSON_UNQUOTE(JSON_EXTRACT(" + svs.field().alias + ".plainAttrs, REPLACE(JSON_UNQUOTE(JSON_SEARCH(" + + svs.field().alias + ".plainAttrs, 'one', '" + + fieldName + "', NULL, '$[*].schema')), '.schema','.values[0]." + + (schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType())) + "')))" + + " AS " + fieldName; item.where = "plainSchema = '" + fieldName + '\''; item.orderBy = fieldName + ' ' + clause.getDirection().name(); } From 318f1d79e31f0cdb973ba6a776bc65e08bcde8e5 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Wed, 3 Sep 2025 11:17:44 +0200 Subject: [PATCH 6/7] fixing myjson and ojson --- .../jpa/dao/MyJPAJSONAnySearchDAO.java | 17 +++++++++++------ .../jpa/dao/OJPAJSONAnySearchDAO.java | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java index b84fe7c4aa0..fe61bec6058 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java @@ -80,12 +80,17 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.field()); - item.select = - "JSON_UNQUOTE(JSON_EXTRACT(" + svs.field().alias + ".plainAttrs, REPLACE(JSON_UNQUOTE(JSON_SEARCH(" - + svs.field().alias + ".plainAttrs, 'one', '" - + fieldName + "', NULL, '$[*].schema')), '.schema','.values[0]." - + (schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType())) + "')))" - + " AS " + fieldName; + item.select = new StringBuilder().append("( SELECT usa"). + append('.'). + append((schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType()))). + append(" FROM "). + append(svs.field().name). + append(" usa WHERE usa.any_id = "). + append(svs.field().alias). + append(".any_id"). + append(" AND usa.plainSchema ='").append(fieldName).append("'"). + append(" LIMIT 1"). + append(") AS ").append(fieldName).toString(); item.where = "plainSchema = '" + fieldName + '\''; item.orderBy = fieldName + ' ' + clause.getDirection().name(); } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java index d08a4acb42c..7bb31c86b99 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java @@ -77,9 +77,18 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.field()); - item.select = svs.field().alias + '.' - + (schema.isUniqueConstraint() ? "u" : "") + key(schema.getType()) - + " AS " + fieldName; + item.select = new StringBuilder().append("( SELECT usa"). + append('.'). + append((schema.isUniqueConstraint() ? "u" : "") + key(schema.getType())). + append(" FROM "). + append(svs.field().name). + append(" usa WHERE usa.any_id = "). + append(svs.field().alias). + append(".any_id"). + append(" AND usa.plainSchema ='").append(fieldName).append("'"). + append(" FETCH FIRST 1 ROWS ONLY "). + append(") AS ").append(fieldName).toString(); + item.where = "plainSchema = '" + fieldName + '\''; item.orderBy = fieldName + ' ' + clause.getDirection().name(); } From 5075e5d31151f8ba57e2da280d684d5f57d465fc Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Wed, 3 Sep 2025 15:06:38 +0200 Subject: [PATCH 7/7] fixes IT tests --- .../syncope/fit/core/UserIssuesITCase.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index d578fe01857..79d23ba03c9 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1928,24 +1928,40 @@ public void issueSYNCOPE1894() { @Test public void issueSYNCOPE1906() { UserCR userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa1")); UserTO aa1 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa2")); UserTO aa2 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa3")); UserTO aa3 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa4")); UserTO aa4 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa5")); UserTO aa5 = createUser(userCR).getEntity(); + + // add also other users with valued ctype + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "a ctype")); + createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "a ctype 2")); + createUser(userCR).getEntity(); try { await().until(() -> USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) @@ -1960,6 +1976,8 @@ public void issueSYNCOPE1906() { .orderBy("ctype DESC") .build()).getResult(); + assertEquals(5, users.size()); + assertEquals(aa1.getUsername(), users.get(4).getUsername()); assertEquals(aa2.getUsername(), users.get(3).getUsername()); assertEquals(aa3.getUsername(), users.get(2).getUsername());