diff --git a/pom.xml b/pom.xml index 0d37407ad..c4a640c49 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,13 @@ ${jackson} + + org.hamcrest + hamcrest-all + ${hamcrest} + test + + diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseOperations.java index 6b4deccbf..ff812dcbd 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseOperations.java @@ -110,8 +110,8 @@ public interface CouchbaseOperations { * objects. Use the provided {@link #queryView} method for more flexibility and direct access.

* * @param design the name of the design document. - * @param view the name of the view. - * @param query the Query object to customize the view query. + * @param view the name of the viewName. + * @param query the Query object to customize the viewName query. * @param entityClass the entity to map to. * @return the converted collection */ @@ -124,12 +124,12 @@ public interface CouchbaseOperations { *

This method is available to ease the working with views by still wrapping exceptions into the Spring * infrastructure.

* - *

It is especially needed if you want to run reduced view queries, because they can't be mapped onto entities + *

It is especially needed if you want to run reduced viewName queries, because they can't be mapped onto entities * directly.

* - * @param design the name of the design document. - * @param view the name of the view. - * @param query the Query object to customize the view query. + * @param design the name of the designDocument document. + * @param view the name of the viewName. + * @param query the Query object to customize the viewName query. * @return */ ViewResponse queryView(String design, String view, Query query); diff --git a/src/main/java/org/springframework/data/couchbase/core/view/View.java b/src/main/java/org/springframework/data/couchbase/core/view/View.java new file mode 100644 index 000000000..d11fdb088 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/view/View.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.core.view; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to support the use of Views with Couchbase. + * + * @author David Harrigan. + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface View { + + /** + * The name of the Design Document to use. + *

+ * This field is mandatory. + * + * @return name of the Design Document. + */ + String designDocument(); + + /** + * The name of the View to use. + *

+ * This field is mandatory. + * + * @return name of the View + */ + String viewName(); + +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java index 9c08f6906..d9ebf9e46 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java @@ -26,4 +26,5 @@ * @author Michael Nitschinger */ public interface CouchbaseRepository extends CrudRepository { + } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java index 6782ea504..24f5a357f 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java @@ -45,6 +45,11 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport { */ private final MappingContext, CouchbasePersistentProperty> mappingContext; + /** + * Holds a custom ViewPostProcessor.. + */ + private final ViewPostProcessor viewPostProcessor; + /** * Create a new factory. * @@ -55,6 +60,9 @@ public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations) this.couchbaseOperations = couchbaseOperations; mappingContext = couchbaseOperations.getConverter().getMappingContext(); + viewPostProcessor = ViewPostProcessor.INSTANCE; + + addRepositoryProxyPostProcessor(viewPostProcessor); } /** @@ -63,17 +71,17 @@ public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations) * @param domainClass the class for the entity. * @param the value type * @param the id type. + * * @return entity information for that domain class. */ @Override - public CouchbaseEntityInformation - getEntityInformation(final Class domainClass) { + public CouchbaseEntityInformation getEntityInformation(final Class domainClass) { CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(domainClass); if (entity == null) { - throw new MappingException(String.format("Could not lookup mapping metadata for domain class %s!", - domainClass.getName())); + throw new MappingException(String.format("Could not lookup mapping metadata for domain class %s!", domainClass.getName())); } + return new MappingCouchbaseEntityInformation((CouchbasePersistentEntity) entity); } @@ -81,23 +89,27 @@ public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations) * Returns a new Repository based on the metadata. * * @param metadata the repository metadata. + * * @return a new created repository. */ @Override protected Object getTargetRepository(final RepositoryMetadata metadata) { - CouchbaseEntityInformation entityInformation = - getEntityInformation(metadata.getDomainType()); - return new SimpleCouchbaseRepository(entityInformation, couchbaseOperations); + CouchbaseEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); + final SimpleCouchbaseRepository simpleCouchbaseRepository = new SimpleCouchbaseRepository(entityInformation, couchbaseOperations); + simpleCouchbaseRepository.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider()); + return simpleCouchbaseRepository; } /** * The base class for this repository. * * @param repositoryMetadata metadata for the repository. + * * @return the base class. */ @Override protected Class getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) { return SimpleCouchbaseRepository.class; } + } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java index a1adefa43..fea6205e7 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java @@ -21,6 +21,7 @@ import com.couchbase.client.protocol.views.ViewResponse; import com.couchbase.client.protocol.views.ViewRow; import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.view.View; import org.springframework.data.couchbase.repository.CouchbaseRepository; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.util.Assert; @@ -46,6 +47,10 @@ public class SimpleCouchbaseRepository implements Co */ private final CouchbaseEntityInformation entityInformation; + /** + * Custom ViewMetadataProvider. + */ + private ViewMetadataProvider viewMetadataProvider; /** * Create a new Repository. @@ -53,8 +58,7 @@ public class SimpleCouchbaseRepository implements Co * @param metadata the Metadata for the entity. * @param couchbaseOperations the reference to the template used. */ - public SimpleCouchbaseRepository(final CouchbaseEntityInformation metadata, - final CouchbaseOperations couchbaseOperations) { + public SimpleCouchbaseRepository(final CouchbaseEntityInformation metadata, final CouchbaseOperations couchbaseOperations) { Assert.notNull(couchbaseOperations); Assert.notNull(metadata); @@ -62,6 +66,15 @@ public SimpleCouchbaseRepository(final CouchbaseEntityInformation met this.couchbaseOperations = couchbaseOperations; } + /** + * Configures a custom {@link ViewMetadataProvider} to be used to detect {@link View}s to be applied to queries. + * + * @param viewMetadataProvider that is used to lookup any annotated View on a query method. + */ + public void setViewMetadataProvider(final ViewMetadataProvider viewMetadataProvider) { + this.viewMetadataProvider = viewMetadataProvider; + } + @Override public S save(S entity) { Assert.notNull(entity, "Entity must not be null!"); @@ -75,7 +88,7 @@ public Iterable save(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); List result = new ArrayList(); - for(S entity : entities) { + for (S entity : entities) { save(entity); result.add(entity); } @@ -109,45 +122,38 @@ public void delete(T entity) { @Override public void delete(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); - for (T entity: entities) { + for (T entity : entities) { couchbaseOperations.remove(entity); } } @Override public Iterable findAll() { - String design = entityInformation.getJavaType().getSimpleName().toLowerCase(); - String view = "all"; - - return couchbaseOperations.findByView(design, view, new Query().setReduce(false), - entityInformation.getJavaType()); + final ResolvedView resolvedView = determineView(); + return couchbaseOperations.findByView(resolvedView.getDesignDocument(), resolvedView.getViewName(), new Query().setReduce(false), entityInformation.getJavaType()); } @Override public Iterable findAll(final Iterable ids) { - String design = entityInformation.getJavaType().getSimpleName().toLowerCase(); - String view = "all"; - Query query = new Query(); query.setReduce(false); query.setKeys(ComplexKey.of(ids)); - return couchbaseOperations.findByView(design, view, query, entityInformation.getJavaType()); + final ResolvedView resolvedView = determineView(); + return couchbaseOperations.findByView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query, entityInformation.getJavaType()); } @Override public long count() { - String design = entityInformation.getJavaType().getSimpleName().toLowerCase(); - String view = "all"; - Query query = new Query(); query.setReduce(true); - ViewResponse response = couchbaseOperations.queryView(design, view, query); + final ResolvedView resolvedView = determineView(); + ViewResponse response = couchbaseOperations.queryView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query); long count = 0; for (ViewRow row : response) { - count += Long.parseLong(row.getValue()); + count += Long.parseLong(row.getValue()); } return count; @@ -155,13 +161,11 @@ public long count() { @Override public void deleteAll() { - String design = entityInformation.getJavaType().getSimpleName().toLowerCase(); - String view = "all"; - Query query = new Query(); query.setReduce(false); - ViewResponse response = couchbaseOperations.queryView(design, view, query); + final ResolvedView resolvedView = determineView(); + ViewResponse response = couchbaseOperations.queryView(resolvedView.getDesignDocument(), resolvedView.getViewName(), query); for (ViewRow row : response) { couchbaseOperations.remove(row.getId()); } @@ -185,4 +189,48 @@ protected CouchbaseEntityInformation getEntityInformation() { return entityInformation; } + /** + * Resolve a View based upon: + *

+ * 1. Any @View annotation that is present + * 2. If none are found, default designDocument to be the entity name (lowercase) and viewName to be "all". + * + * @return ResolvedView containing the designDocument and viewName. + */ + private ResolvedView determineView() { + String designDocument = entityInformation.getJavaType().getSimpleName().toLowerCase(); + String viewName = "all"; + + final View view = viewMetadataProvider.getView(); + + if (view != null) { + designDocument = view.designDocument(); + viewName = view.viewName(); + } + + return new ResolvedView(designDocument, viewName); + } + + /** + * Simple holder to allow an easier exchange of information. + */ + private final class ResolvedView { + + private final String designDocument; + private final String viewName; + + public ResolvedView(final String designDocument, final String viewName) { + this.designDocument = designDocument; + this.viewName = viewName; + } + + private String getDesignDocument() { + return designDocument; + } + + private String getViewName() { + return viewName; + } + } + } diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/ViewMetadataProvider.java b/src/main/java/org/springframework/data/couchbase/repository/support/ViewMetadataProvider.java new file mode 100644 index 000000000..a47384f11 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/support/ViewMetadataProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.repository.support; + +import org.springframework.data.couchbase.core.view.View; + +/** + * Interface to abstract {@link ViewMetadataProvider} that provides {@link View}s to be used for query execution. + * + * @author David Harrigan. + */ +public interface ViewMetadataProvider { + + /** + * Returns the {@link View} to be used. + * + * @return the View, or null if the method hasn't been annotated with @View. + */ + View getView(); + +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/ViewPostProcessor.java b/src/main/java/org/springframework/data/couchbase/repository/support/ViewPostProcessor.java new file mode 100644 index 000000000..da6c54ce6 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/support/ViewPostProcessor.java @@ -0,0 +1,109 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.repository.support; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.interceptor.ExposeInvocationInterceptor; +import org.springframework.core.NamedThreadLocal; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.couchbase.core.view.View; +import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link RepositoryProxyPostProcessor} that sets up an interceptor to read {@link View} information from the + * invoked method. This is necessary to allow redeclaration of CRUD methods in repository interfaces and configure + * view information on them. + * + * @author David Harrigan. + */ +public enum ViewPostProcessor implements RepositoryProxyPostProcessor { + + INSTANCE; + + private static final ThreadLocal> VIEW_METADATA = new NamedThreadLocal>("View Metadata"); + + @Override + public void postProcess(final ProxyFactory factory) { + factory.addAdvice(ExposeInvocationInterceptor.INSTANCE); + factory.addAdvice(ViewInterceptor.INSTANCE); + } + + public ViewMetadataProvider getViewMetadataProvider() { + return ThreadBoundViewMetadata.INSTANCE; + } + + /** + * {@link MethodInterceptor} to inspect the currently invoked {@link Method} for a {@link View} annotation. + *

+ * If a View annotation is found, it will bind it to a locally held ThreadLocal for later lookup in the + * SimpleCouchbaseRepository class. + * + * @author David Harrigan. + */ + static enum ViewInterceptor implements MethodInterceptor { + + INSTANCE; + + @Override + public Object invoke(final MethodInvocation invocation) throws Throwable { + + final View view = AnnotationUtils.getAnnotation(invocation.getMethod(), View.class); + if (view != null) { + Map map = VIEW_METADATA.get(); + if (map == null) { + map = new HashMap(); + VIEW_METADATA.set(map); + } + map.put(invocation.getMethod(), view); + } + try { + return invocation.proceed(); + } finally { + VIEW_METADATA.remove(); + } + + } + + } + + /** + * {@link ViewMetadataProvider} that looks up a bound View from a locally held ThreadLocal, using + * the current method invocationas as the key. If not bound View is found, a null is returned. + * + * @author David Harrigan. + */ + private static enum ThreadBoundViewMetadata implements ViewMetadataProvider { + + INSTANCE; + + @Override + public View getView() { + final MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation(); + final Map map = VIEW_METADATA.get(); + return (map == null) ? null : (View) map.get(invocation.getMethod()); + } + + } + + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java index 2a8601581..ada4d1450 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java @@ -35,20 +35,23 @@ public void beforeTestClass(final TestContext testContext) throws Exception { createAndWaitForDesignDocs(client); } - private void populateTestData(CouchbaseClient client) { + private void populateTestData(final CouchbaseClient client) { CouchbaseTemplate template = new CouchbaseTemplate(client); - - for(int i=0;i < 100; i++) { - User u = new User("testuser-" + i, "uname" + i); - template.save(u); + for (int i = 0; i < 100; i++) { + template.save(new User("testuser-" + i, "uname-" + i)); } } - private void createAndWaitForDesignDocs(CouchbaseClient client) { + private void createAndWaitForDesignDocs(final CouchbaseClient client) { DesignDocument designDoc = new DesignDocument("user"); - String mapFunction = "function (doc, meta) { if(doc._class == " - + "\"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }"; - designDoc.setView(new ViewDesign("all", mapFunction, "_count")); + String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }"; + designDoc.setView(new ViewDesign("customFindAllView", mapFunction, "_count")); + + client.createDesignDoc(designDoc); + + designDoc = new DesignDocument("userCustom"); + designDoc.setView(new ViewDesign("customCountView", mapFunction, "_count")); + client.createDesignDoc(designDoc); } diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java new file mode 100644 index 000000000..15a6dae1f --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.repository; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.client.protocol.views.Query; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.couchbase.TestApplicationConfig; +import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static com.couchbase.client.protocol.views.Stale.FALSE; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author David Harrigan + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestApplicationConfig.class) +@TestExecutionListeners(CouchbaseRepositoryViewListener.class) +public class CouchbaseRepositoryViewTests { + + @Autowired + private CouchbaseClient client; + + @Autowired + private CouchbaseTemplate template; + + private CustomUserRepository repository; + + @Before + public void setup() throws Exception { + repository = new CouchbaseRepositoryFactory(template).getRepository(CustomUserRepository.class); + } + + @Test + public void shouldFindAllWithCustomView() { + client.query(client.getView("user", "customFindAllView"), new Query().setStale(FALSE)); + Iterable allUsers = repository.findAll(); + int i = 0; + for (final User allUser : allUsers) { + i++; + } + assertThat(i, is(100)); + } + + @Test + public void shouldCountWithCustomView() { + client.query(client.getView("userCustom", "customCountView"), new Query().setStale(FALSE)); + final long value = repository.count(); + assertThat(value, is(100L)); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CustomUserRepository.java b/src/test/java/org/springframework/data/couchbase/repository/CustomUserRepository.java new file mode 100644 index 000000000..ee1081e08 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/CustomUserRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.repository; + +import org.springframework.data.couchbase.core.view.View; + +/** + * @author David Harrigan + */ +public interface CustomUserRepository extends CouchbaseRepository { + + @Override + @View(designDocument = "user", viewName = "customFindAllView") + Iterable findAll(); + + @Override + @View(designDocument = "userCustom", viewName = "customCountView") + long count(); + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java new file mode 100644 index 000000000..3a4377ff2 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013 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. + * 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.springframework.data.couchbase.repository; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.client.protocol.views.DesignDocument; +import com.couchbase.client.protocol.views.ViewDesign; +import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; + +/** + * @author Michael Nitschinger + */ +public class SimpleCouchbaseRepositoryListener extends DependencyInjectionTestExecutionListener { + + @Override + public void beforeTestClass(final TestContext testContext) throws Exception { + CouchbaseClient client = (CouchbaseClient) testContext.getApplicationContext().getBean("couchbaseClient"); + populateTestData(client); + createAndWaitForDesignDocs(client); + } + + private void populateTestData(CouchbaseClient client) { + CouchbaseTemplate template = new CouchbaseTemplate(client); + + for (int i = 0; i < 100; i++) { + User u = new User("testuser-" + i, "uname-" + i); + template.save(u); + } + } + + private void createAndWaitForDesignDocs(CouchbaseClient client) { + DesignDocument designDoc = new DesignDocument("user"); + String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }"; + designDoc.setView(new ViewDesign("all", mapFunction, "_count")); + client.createDesignDoc(designDoc); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java index 90883dae2..cc7d75d5f 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java @@ -38,14 +38,12 @@ */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestApplicationConfig.class) -@TestExecutionListeners(CouchbaseRepositoryViewListener.class) +@TestExecutionListeners(SimpleCouchbaseRepositoryListener.class) public class SimpleCouchbaseRepositoryTests { - @Autowired private CouchbaseClient client; - @Autowired private CouchbaseTemplate template; @@ -75,6 +73,9 @@ public void simpleCrud() { } @Test + /** + * This test uses/assumes a default viewName called "all" that is configured on Couchbase. + */ public void shouldFindAll() { // do a non-stale query to populate data for testing. client.query(client.getView("user", "all"), new Query().setStale(Stale.FALSE)); diff --git a/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java b/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java index dbbf55a3e..da78c6f76 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java +++ b/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java @@ -19,6 +19,6 @@ /** * @author Michael Nitschinger */ -public interface UserRepository extends CouchbaseRepository{ +public interface UserRepository extends CouchbaseRepository { }