diff --git a/core/src/main/java/org/springframework/ldap/core/LdapOperations.java b/core/src/main/java/org/springframework/ldap/core/LdapOperations.java index 70298ef2..33cec02c 100644 --- a/core/src/main/java/org/springframework/ldap/core/LdapOperations.java +++ b/core/src/main/java/org/springframework/ldap/core/LdapOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ import javax.naming.directory.Attributes; import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import java.util.List; +import java.util.stream.Stream; /** * Interface that specifies a basic set of LDAP operations. Implemented by @@ -1693,6 +1694,37 @@ public interface LdapOperations { */ T searchForObject(LdapQuery query, ContextMapper mapper); + /** + * Perform a search with parameters from the specified LdapQuery. The Attributes of the found entries will be + * supplied to the AttributesMapper for processing, and all + * returned objects will be collected in a list to be returned. + * + * @param query the LDAP query specification. + * @param mapper the Attributes to supply all found Attributes to. + * @return a Stream of all entries received from the + * Attributes. + * + * @throws NamingException if any error occurs. + * @since 3.0 + * @see org.springframework.ldap.query.LdapQueryBuilder + */ + Stream searchForStream(LdapQuery query, AttributesMapper mapper); + + /** + * Perform a search with parameters from the specified LdapQuery. All found objects will be supplied to the + * ContextMapper for processing, and all returned objects will be collected in a list to be returned. + * + * @param query the LDAP query specification. + * @param mapper the ContextMapper to supply all found entries to. + * @return a Stream of all entries received from the + * ContextMapper. + * + * @throws NamingException if any error occurs. + * @since 3.0 + * @see org.springframework.ldap.query.LdapQueryBuilder + */ + Stream searchForStream(LdapQuery query, ContextMapper mapper); + /** * Read a named entry from the LDAP directory. The referenced class must have object-directory mapping metadata * specified using {@link org.springframework.ldap.odm.annotations.Entry} and associated annotations. @@ -1844,6 +1876,24 @@ public interface LdapOperations { */ T findOne(LdapQuery query, Class clazz); + /** + * Search for entries in the LDAP directory. The referenced class must have object-directory + * mapping metadata specified using {@link org.springframework.ldap.odm.annotations.Entry} and associated annotations. + *

+ * Only those entries that both match the query search filter and + * are represented by the given Java class are returned. + * + * @param The Java type to return + * @param query the LDAP query specification + * @param clazz The Java type to return + * @return All matching entries. + * + * @throws org.springframework.ldap.NamingException on error. + * @see org.springframework.ldap.query.LdapQueryBuilder + * @since 3.0 + */ + Stream findForStream(LdapQuery query, Class clazz); + /** * Get the configured ObjectDirectoryMapper. For internal use. * diff --git a/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java b/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java index 215003bd..8410c130 100644 --- a/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java +++ b/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2013 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,10 +28,10 @@ import org.springframework.ldap.odm.core.ObjectDirectoryMapper; import org.springframework.ldap.odm.core.OdmException; import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper; import org.springframework.ldap.query.LdapQuery; +import org.springframework.ldap.query.LdapQueryBuilder; import org.springframework.ldap.support.LdapUtils; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; import javax.naming.Binding; import javax.naming.Name; @@ -44,10 +44,16 @@ import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapName; -import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Executes core LDAP functionality and helps to avoid common errors, relieving @@ -1684,6 +1690,53 @@ public class LdapTemplate implements LdapOperations, InitializingBean { mapper); } + /** + * {@inheritDoc} + */ + @Override + public Stream searchForStream(LdapQuery query, AttributesMapper attributesMapper) { + return searchForStream(query, (SearchResult result) -> { + Attributes attributes = result.getAttributes(); + return unchecked(() -> attributesMapper.mapFromAttributes(attributes)); + }); + } + + /** + * {@inheritDoc} + */ + @Override + public Stream searchForStream(LdapQuery query, ContextMapper mapper) { + return searchForStream(query, (SearchResult result) -> { + Object object = result.getObject(); + if (object == null) { + throw new ObjectRetrievalException("Binding did not contain any object."); + } + return unchecked(() -> mapper.mapFromContext(object)); + }); + } + + Stream searchForStream(LdapQuery query, Function mapper) { + Name base = query.base(); + Filter filter = query.filter(); + SearchControls searchControls = searchControlsForQuery(query, RETURN_OBJ_FLAG); + DirContext ctx = contextSource.getReadOnlyContext(); + String encodedFilter = filter.encode(); + + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Searching - base=%1$s, finalFilter=%2$s, scope=%3$s", base, filter, searchControls)); + } + + assureReturnObjFlagSet(searchControls); + + NamingEnumeration results = unchecked(() -> ctx.search(base, encodedFilter, searchControls)); + if (results == null) { + return Stream.empty(); + } + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(CollectionUtils.toIterator(results), Spliterator.ORDERED), false) + .map((nameClassPair) -> unchecked(() -> mapper.apply(nameClassPair))) + .filter(Objects::nonNull).onClose(() -> closeContextAndNamingEnumeration(ctx, results)); + } + /** * {@inheritDoc} */ @@ -1884,6 +1937,51 @@ public class LdapTemplate implements LdapOperations, InitializingBean { return result.get(0); } + /** + * {@inheritDoc} + */ + @Override + public Stream findForStream(LdapQuery query, Class clazz) { + LdapQueryBuilder builder = LdapQueryBuilder.fromQuery(query); + if (query.attributes() == null) { + String[] attributes = odm.manageClass(clazz); + builder.attributes(attributes); + } + Filter includeClass = odm.filterFor(clazz, query.filter()); + ContextMapper contextMapper = (object) -> odm.mapFromLdapDataEntry((DirContextOperations) object, clazz); + return searchForStream(builder.filter(includeClass), contextMapper); + } + + private T unchecked(CheckedSupplier supplier) { + try { + return supplier.get(); + } catch (NameNotFoundException e) { + // It is possible to ignore errors caused by base not found + if (!ignoreNameNotFoundException) { + throw LdapUtils.convertLdapException(e); + } + LOG.warn("Base context not found, ignoring: " + e.getMessage()); + } catch (PartialResultException e) { + // Workaround for AD servers not handling referrals correctly. + if (!ignorePartialResultException) { + throw LdapUtils.convertLdapException(e); + } + LOG.debug("PartialResultException encountered and ignored", e); + } catch(SizeLimitExceededException e) { + if(!ignoreSizeLimitExceededException) { + throw LdapUtils.convertLdapException(e); + } + LOG.debug("SizeLimitExceededException encountered and ignored", e); + } catch (javax.naming.NamingException e) { + throw LdapUtils.convertLdapException(e); + } + return null; + } + + private interface CheckedSupplier { + T get() throws javax.naming.NamingException; + } + /** * The status of an authentication attempt. * diff --git a/core/src/main/java/org/springframework/ldap/query/LdapQueryBuilder.java b/core/src/main/java/org/springframework/ldap/query/LdapQueryBuilder.java index ba2f9e91..e4ffa56d 100644 --- a/core/src/main/java/org/springframework/ldap/query/LdapQueryBuilder.java +++ b/core/src/main/java/org/springframework/ldap/query/LdapQueryBuilder.java @@ -78,6 +78,26 @@ public final class LdapQueryBuilder implements LdapQuery { return new LdapQueryBuilder(); } + /** + * Construct a new LdapQueryBuilder based on an existing {@link LdapQuery} + * All non-filter fields are copied. + * @return a new instance. + * @since 3.0 + */ + public static LdapQueryBuilder fromQuery(LdapQuery query) { + LdapQueryBuilder builder = LdapQueryBuilder.query() + .attributes(query.attributes()) + .base(query.base()); + if (query.countLimit() != null) { + builder.countLimit(query.countLimit()); + } + builder.searchScope(query.searchScope()); + if (query.timeLimit() != null) { + builder.timeLimit(query.timeLimit()); + } + return builder; + } + /** * Set the base search path for the query. * Default is {@link org.springframework.ldap.support.LdapUtils#emptyLdapName()}. diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index ffa5d3e3..35a0acc6 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -1158,6 +1158,10 @@ public class OdmPersonRepo { public List findByLastName(String lastName) { return ldapTemplate.find(query().where("sn").is(lastName), Person.class); } + + public Stream streamFindByLastName(String lastName) { + return ldapTemplate.findStream(query().where("sn").is(lastName), Person.class); + } } ---- ==== diff --git a/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java b/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java index f025ed6f..55c96346 100644 --- a/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java +++ b/test-support/src/main/java/org/springframework/ldap/test/AttributeCheckAttributesMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ public class AttributeCheckAttributesMapper implements AttributesMapper Assert.assertNull(attributes.get(absentAttribute)); } - return null; + return new Object(); } public void setAbsentAttributes(String[] absentAttributes) { diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultITest.java b/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultITest.java index d4a8936f..19747d29 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultITest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultITest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2016 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ import javax.naming.Name; import javax.naming.NamingException; import javax.naming.directory.SearchControls; import java.util.List; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -112,6 +113,18 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_FewerAttributes() { attributesMapper.setExpectedAttributes(new String[] {"cn"}); @@ -125,6 +138,19 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_FewerAttributes() { + attributesMapper.setExpectedAttributes(new String[] {"cn"}); + attributesMapper.setExpectedValues(new String[]{"Some Person2"}); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .attributes("cn") + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_SearchScope() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -138,6 +164,19 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_LdapQuery_AttributesMapper_SearchScope_CorrectBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -151,6 +190,19 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope_CorrectBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base("ou=company1,ou=Sweden") + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_NoBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -162,6 +214,17 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_NoBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_DifferentBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -174,6 +237,18 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_DifferentBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base("ou=Norway") + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_SearchScope_AttributesMapper() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -291,6 +366,17 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base(BASE_NAME) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_ContextMapper_LdapQuery_NoBase() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -301,6 +387,16 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_NoBase() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_ContextMapper_LdapQuery_SearchScope() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -313,6 +409,18 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_SearchScope() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base(BASE_NAME) + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_ContextMapper_LdapQuery_SearchScope_CorrectBase() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -325,6 +433,18 @@ public class LdapTemplateSearchResultITest extends AbstractLdapTemplateIntegrati assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_SearchScope_CorrectBase() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base("ou=company1,ou=Sweden") + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearchForContext_LdapQuery() { DirContextOperations result = tested.searchForContext(query() diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultNamespaceConfigITest.java b/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultNamespaceConfigITest.java index c0ff72cb..9f640901 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultNamespaceConfigITest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/itest/LdapTemplateSearchResultNamespaceConfigITest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2016 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import org.springframework.test.context.ContextConfiguration; import javax.naming.Name; import javax.naming.directory.SearchControls; import java.util.List; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -109,6 +110,18 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_FewerAttributes() { attributesMapper.setExpectedAttributes(new String[] {"cn"}); @@ -122,6 +135,19 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_FewerAttributes() { + attributesMapper.setExpectedAttributes(new String[] {"cn"}); + attributesMapper.setExpectedValues(new String[]{"Some Person2"}); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .attributes("cn") + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_SearchScope() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -135,6 +161,19 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base(BASE_STRING) + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_LdapQuery_AttributesMapper_SearchScope_CorrectBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -148,6 +187,19 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_SearchScope_CorrectBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base("ou=company1,ou=Sweden") + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_NoBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -159,6 +211,17 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_NoBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_LdapQuery_AttributesMapper_DifferentBase() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -171,6 +234,18 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_LdapQuery_AttributesMapper_DifferentBase() { + attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); + attributesMapper.setExpectedValues(ALL_VALUES); + + List list = tested.searchForStream(query() + .base("ou=Norway") + .where("objectclass").is("person").and("sn").is("Person2"), + attributesMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_SearchScope_AttributesMapper() { attributesMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -288,6 +363,17 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base(BASE_NAME) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_ContextMapper_LdapQuery_NoBase() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -298,6 +384,16 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_NoBase() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearch_ContextMapper_LdapQuery_SearchScope() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -310,6 +406,18 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).isEmpty(); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_SearchScope() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base(BASE_NAME) + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).isEmpty(); + } + @Test public void testSearch_ContextMapper_LdapQuery_SearchScope_CorrectBase() { contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); @@ -322,6 +430,18 @@ public class LdapTemplateSearchResultNamespaceConfigITest extends AbstractLdapTe assertThat(list).hasSize(1); } + @Test + public void testSearchForStream_ContextMapper_LdapQuery_SearchScope_CorrectBase() { + contextMapper.setExpectedAttributes(ALL_ATTRIBUTES); + contextMapper.setExpectedValues(ALL_VALUES); + List list = tested.searchForStream(query() + .base("ou=company1,ou=Sweden") + .searchScope(SearchScope.ONELEVEL) + .where("objectclass").is("person").and("sn").is("Person2"), + contextMapper).collect(Collectors.toList()); + assertThat(list).hasSize(1); + } + @Test public void testSearchForContext_LdapQuery() { DirContextOperations result = tested.searchForContext(query() diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithDnAnnotationsITest.java b/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithDnAnnotationsITest.java index 2ea52952..4a30e4e7 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithDnAnnotationsITest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithDnAnnotationsITest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2016 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import static org.springframework.ldap.query.LdapQueryBuilder.query; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -88,6 +89,23 @@ public class LdapTemplateOdmWithDnAnnotationsITest extends AbstractLdapTemplateI assertThat(person.getEntryUuid()).describedAs("The operational attribute 'entryUUID' was not set").isNotEmpty(); } + @Test + public void testFindForStreamInCountry() { + List persons = tested.findForStream(query() + .base("ou=Sweden") + .where("cn").isPresent(), PersonWithDnAnnotations.class) + .collect(Collectors.toList()); + + assertThat(persons).hasSize(4); + + PersonWithDnAnnotations person = findPerson(persons, "Some Person3"); + + // Automatically calculated + assertThat(person.getCompany()).isEqualTo("company1"); + assertThat(person.getCountry()).isEqualTo("Sweden"); + assertThat(person.getEntryUuid()).describedAs("The operational attribute 'entryUUID' was not set").isNotEmpty(); + } + private PersonWithDnAnnotations findPerson(List persons, String cn) { for (PersonWithDnAnnotations person : persons) { if(person.getCommonName().equals(cn)) { diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithNoDnAnnotationsITest.java b/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithNoDnAnnotationsITest.java index 96ce5901..dac29f0f 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithNoDnAnnotationsITest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/itest/odm/LdapTemplateOdmWithNoDnAnnotationsITest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2016 the original author or authors. + * Copyright 2005-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import static org.springframework.ldap.query.LdapQueryBuilder.query; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -93,6 +94,23 @@ public class LdapTemplateOdmWithNoDnAnnotationsITest extends AbstractLdapTemplat assertThat(person.getEntryUuid()).describedAs("The operational attribute 'entryUUID' was not set").isNotEmpty(); } + @Test + public void testFindForStream() { + List persons = tested.findForStream(query() + .where("cn").is("Some Person3"), Person.class) + .collect(Collectors.toList()); + + assertThat(persons).hasSize(1); + Person person = persons.get(0); + + assertThat(person).isNotNull(); + assertThat(person.getCommonName()).isEqualTo("Some Person3"); + assertThat(person.getSurname()).isEqualTo("Person3"); + assertThat(person.getDesc().get(0)).isEqualTo("Sweden, Company1, Some Person3"); + assertThat(person.getTelephoneNumber()).isEqualTo("+46 555-123654"); + assertThat(person.getEntryUuid()).describedAs("The operational attribute 'entryUUID' was not set").isNotEmpty(); + } + @Test public void testFindInCountry() { List persons = tested.find(query() @@ -105,6 +123,19 @@ public class LdapTemplateOdmWithNoDnAnnotationsITest extends AbstractLdapTemplat assertThat(person).isNotNull(); } + @Test + public void testFindForStreamInCountry() { + List persons = tested.findForStream(query() + .base("ou=Sweden") + .where("cn").isPresent(), Person.class) + .collect(Collectors.toList()); + + assertThat(persons).hasSize(4); + Person person = persons.get(0); + + assertThat(person).isNotNull(); + } + @Test public void testFindAll() { List result = tested.findAll(Person.class);