Skip to content

Commit

Permalink
allow to include / exclude write operations on a document (#639)
Browse files Browse the repository at this point in the history
added flags to repository annotation (@Mongo.Repository) which ...
  • Loading branch information
asereda-gs authored and elucash committed Jun 12, 2017
1 parent 43d7c18 commit 5aab94f
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 45 deletions.
24 changes: 24 additions & 0 deletions mongo/src/org/immutables/mongo/Mongo.java
Expand Up @@ -46,8 +46,32 @@
* Specify document collection name. If not specified, then collection name will be given * Specify document collection name. If not specified, then collection name will be given
* automatically by document class name, i.e. {@code "myDocument"} for {@code MyDocument} class. * automatically by document class name, i.e. {@code "myDocument"} for {@code MyDocument} class.
* @return name * @return name
* @deprecated use {@link #collection()} attribute instead
*/ */
@Deprecated
String value() default ""; String value() default "";

/**
* Specify document collection name. If not specified, then collection name will be given
* automatically by document class name, i.e. {@code "myDocument"} for {@code MyDocument} class.
* @return name
*/
String collection() default "";

/**
* Force repository to be readonly. In this case no collection write methods (eg. {@code insert},
* {@code update}, {@code deleteAll} etc.) will be generated.
*
* @return true for a repository without write actions, false otherwise.
*/
boolean readonly() default false;

/**
* Generate index helper which builds mongo indexes.
*
* @return true for index operations to be available to the repository, false otherwise.
*/
boolean index() default true;
} }


/** /**
Expand Down
75 changes: 45 additions & 30 deletions mongo/src/org/immutables/mongo/repository/Repositories.java
Expand Up @@ -380,7 +380,7 @@ private Constraints.Constraint appendFields(
} }


/** /**
* Base updater. * Base class which handles update operations (like {@code upsert}, {@code updateAll}, {@code updateFirst} etc.)
* @param <T> document type * @param <T> document type
*/ */
@NotThreadSafe @NotThreadSafe
Expand Down Expand Up @@ -557,7 +557,7 @@ public final FluentFuture<Optional<T>> update() {
} }


/** /**
* Base class for the indexer objects. * Base class for the indexer objects. Allows to create indexes for mongo documents at runtime.
* @param <T> document type * @param <T> document type
* @param <I> a self type of extended indexer class * @param <I> a self type of extended indexer class
*/ */
Expand All @@ -573,7 +573,7 @@ protected Indexer(Repository<T> repository) {
/** /**
* Configures name for an index, that is otherwise will be auto-named by index fields. * Configures name for an index, that is otherwise will be auto-named by index fields.
* @param indexName explicitly provided index name * @param indexName explicitly provided index name
* @return {@code this} indexer for chained invocation indexer * @return {@code this} indexer for chained invocation
*/ */
// safe unchecked: we expect I to be a self type // safe unchecked: we expect I to be a self type
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Expand Down Expand Up @@ -619,14 +619,55 @@ public final FluentFuture<Void> ensure() {
} }
} }


/**
* Fetcher class which adds delete functionality to the base class {@link Finder}.
*
* @param <T> document type
* @param <F> a self type of extended finder class
*/
@NotThreadSafe
public static abstract class FinderWithDelete<T, F extends Finder<T, F>> extends Finder<T, F> {

protected FinderWithDelete(Repository<T> repository) {
super(repository);
}

/**
* Delete all matching documents from the collection if they matches {@link Criteria}.
* @return future of number of deleted documents if WriteConcern allows.
*/
public FluentFuture<Integer> deleteAll() {
checkState(numberToSkip == 0, "Cannot use .skip() with .deleteAll()");
return repository.doDelete(criteria);
}

/**
* Deletes and returns first matching document. Returns {@link Optional#absent()} if none
* documents matches.
* @return future of optional matching deleted document.
*/
public FluentFuture<Optional<T>> deleteFirst() {
checkState(numberToSkip == 0, "Cannot use .skip() with .deleteFirst()");
return repository.doModify(
criteria,
ordering,
exclusion,
Constraints.nilConstraint(),
false,
false,
true);
}

}

/** /**
* Base class for the finder objects. Fetcher objects are used to configure query. * Base class for the finder objects. Fetcher objects are used to configure query.
* @param <T> document type * @param <T> document type
* @param <F> a self type of extended finder class * @param <F> a self type of extended finder class
*/ */
@NotThreadSafe @NotThreadSafe
public static abstract class Finder<T, F extends Finder<T, F>> extends Operation<T> { public static abstract class Finder<T, F extends Finder<T, F>> extends Operation<T> {
private int numberToSkip; int numberToSkip;


@Nullable @Nullable
protected Constraints.ConstraintHost criteria; protected Constraints.ConstraintHost criteria;
Expand Down Expand Up @@ -692,31 +733,5 @@ public Optional<T> apply(List<T> input) {
} }
}); });
} }

/**
* Delete all matching documents from the collection if they matches {@link Criteria}.
* @return future of number of deleted documents if WriteConcern allows.
*/
public FluentFuture<Integer> deleteAll() {
checkState(numberToSkip == 0, "Cannot use .skip() with .deleteAll()");
return repository.doDelete(criteria);
}

/**
* Deletes and returns first matching document. Returns {@link Optional#absent()} if none
* documents matches.
* @return future of optional matching deleted document.
*/
public FluentFuture<Optional<T>> deleteFirst() {
checkState(numberToSkip == 0, "Cannot use .skip() with .deleteFirst()");
return repository.doModify(
criteria,
ordering,
exclusion,
Constraints.nilConstraint(),
false,
false,
true);
}
} }
} }
83 changes: 83 additions & 0 deletions mongo/test/org/immutables/mongo/fixture/flags/FlagsTest.java
@@ -0,0 +1,83 @@
package org.immutables.mongo.fixture.flags;

import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import org.immutables.mongo.fixture.MongoContext;
import org.junit.Rule;
import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Set;

import static org.immutables.check.Checkers.check;

/**
* Ensures read / write / index methods are present / absent from generated repositories after some flags have been
* set using {@link org.immutables.mongo.Mongo.Repository} annotation.
*/
public class FlagsTest {

@Rule
public final MongoContext context = MongoContext.create();

@Test
public void standard() throws Exception {
StandardRepository repo = new StandardRepository(context.setup());

repo.index().withName().ensure().getUnchecked();

repo.findAll().deleteAll().getUnchecked();
repo.findAll().deleteFirst().getUnchecked();
repo.findAll().andModifyFirst().setName("foo").update();
repo.findAll().andReplaceFirst(ImmutableStandard.builder().id("id1").build()).upsert().getUnchecked();

}

@Test
public void readonly() throws Exception {
ReadonlyRepository repo = new ReadonlyRepository(context.setup());

// index method should be present. Make it compile time check
repo.index().withName().ensure().getUnchecked();

// ensure write methods are missing
check(methodsByName(ReadonlyRepository.class, "andModifyFirst")).isEmpty();
check(methodsByName(ReadonlyRepository.class, "andReplaceFirst")).isEmpty();
check(methodsByName(ReadonlyRepository.class, "update")).isEmpty();
check(methodsByName(ReadonlyRepository.class, "upsert")).isEmpty();
check(methodsByName(ReadonlyRepository.class, "insert")).isEmpty();

check(methodsByName(ReadonlyRepository.Finder.class, "deleteFirst")).isEmpty();
check(methodsByName(ReadonlyRepository.Finder.class, "deleteAll")).isEmpty();
check(methodsByName(ReadonlyRepository.Finder.class, "delete")).isEmpty();
check(methodsByName(ReadonlyRepository.Finder.class, "andModifyFirst")).isEmpty();
check(methodsByName(ReadonlyRepository.Finder.class, "andReplaceFirst")).isEmpty();
}

@Test
public void noIndex() throws Exception {
NoIndexRepository repo = new NoIndexRepository(context.setup());

repo.find(repo.criteria().id("foo")).fetchAll().getUnchecked();
check(methodsByName(NoIndexRepository.class, "index")).isEmpty();
}

/**
* Gets all (public) methods by name using reflection
*/
private static Set<Method> methodsByName(Class<?> type, final String name) {
return FluentIterable
.from(Arrays.asList(type.getDeclaredMethods()))
.append(Arrays.asList(type.getMethods()))
.filter(new Predicate<Method>() {
@Override
public boolean apply(Method method) {
return Modifier.isPublic(method.getModifiers()) && method.getName().equals(name);
}
})
.toSet();
}

}
36 changes: 36 additions & 0 deletions mongo/test/org/immutables/mongo/fixture/flags/Repo.java
@@ -0,0 +1,36 @@
package org.immutables.mongo.fixture.flags;

import com.google.common.base.Optional;
import org.immutables.mongo.Mongo;
import org.immutables.value.Value;

public interface Repo {

@Mongo.Id
String id();

Optional<String> name();

@Mongo.Repository
@Value.Immutable
interface Standard extends Repo {

}

/**
* Repository without delete / andModifyFirst / andReplaceFirst methods.
*/
@Mongo.Repository(readonly = true)
@Value.Immutable
interface Readonly extends Repo {

}


@Mongo.Repository(index = false, readonly = true)
@Value.Immutable
interface NoIndex extends Repo {

}

}
20 changes: 20 additions & 0 deletions mongo/test/org/immutables/mongo/fixture/flags/package-info.java
@@ -0,0 +1,20 @@
/*
Copyright 2013-2015 Immutables Authors and Contributors
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.
*/

/**
* Tests repositories with various flags (readonly, index etc.)
*/
package org.immutables.mongo.fixture.flags;

0 comments on commit 5aab94f

Please sign in to comment.