JAMES-1781 Cassandra implementation for partial updates
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/6da59847 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/6da59847 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/6da59847 Branch: refs/heads/master Commit: 6da598471ac2f3632ae9c341b5b5a42e6fcec451 Parents: 50e3ab5 Author: Benoit Tellier <[email protected]> Authored: Mon Jun 27 19:02:53 2016 +0700 Committer: Benoit Tellier <[email protected]> Committed: Thu Sep 29 12:48:14 2016 +0200 ---------------------------------------------------------------------- .../vacation/CassandraVacationDAO.java | 66 +++++++++++++------- .../vacation/CassandraVacationRepository.java | 13 +++- 2 files changed, 55 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/6da59847/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationDAO.java ---------------------------------------------------------------------- diff --git a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationDAO.java b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationDAO.java index e52139c..2ac0823 100644 --- a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationDAO.java +++ b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationDAO.java @@ -27,6 +27,7 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select; import java.time.ZonedDateTime; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import javax.inject.Inject; @@ -36,51 +37,53 @@ import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor; import org.apache.james.backends.cassandra.utils.ZonedDateTimeRepresentation; import org.apache.james.jmap.api.vacation.AccountId; import org.apache.james.jmap.api.vacation.Vacation; +import org.apache.james.jmap.api.vacation.VacationPatch; import org.apache.james.jmap.cassandra.vacation.tables.CassandraVacationTable; +import org.apache.james.util.FunctionGenerator; +import org.apache.james.util.PatchedValue; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.UserType; +import com.datastax.driver.core.querybuilder.Insert; +import com.google.common.collect.ImmutableList; public class CassandraVacationDAO { private final CassandraAsyncExecutor cassandraAsyncExecutor; - private final PreparedStatement insertStatement; private final PreparedStatement readStatement; private final UserType zonedDateTimeUserType; + private final FunctionGenerator<VacationPatch, Insert> insertGeneratorPipeline; @Inject public CassandraVacationDAO(Session session, CassandraTypesProvider cassandraTypesProvider) { this.zonedDateTimeUserType = cassandraTypesProvider.getDefinedUserType(CassandraZonedDateTimeModule.ZONED_DATE_TIME); this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session); - this.insertStatement = session.prepare(insertInto(CassandraVacationTable.TABLE_NAME) - .value(CassandraVacationTable.ACCOUNT_ID, bindMarker(CassandraVacationTable.ACCOUNT_ID)) - .value(CassandraVacationTable.IS_ENABLED, bindMarker(CassandraVacationTable.IS_ENABLED)) - .value(CassandraVacationTable.FROM_DATE, bindMarker(CassandraVacationTable.FROM_DATE)) - .value(CassandraVacationTable.TO_DATE, bindMarker(CassandraVacationTable.TO_DATE)) - .value(CassandraVacationTable.TEXT, bindMarker(CassandraVacationTable.TEXT)) - .value(CassandraVacationTable.SUBJECT, bindMarker(CassandraVacationTable.SUBJECT)) - .value(CassandraVacationTable.HTML, bindMarker(CassandraVacationTable.HTML))); - this.readStatement = session.prepare(select() .from(CassandraVacationTable.TABLE_NAME) .where(eq(CassandraVacationTable.ACCOUNT_ID, bindMarker(CassandraVacationTable.ACCOUNT_ID)))); + + insertGeneratorPipeline = ImmutableList.<FunctionGenerator<VacationPatch, Insert>>of( + patch -> applyPatchForField(CassandraVacationTable.SUBJECT, patch.getSubject()), + patch -> applyPatchForField(CassandraVacationTable.HTML, patch.getHtmlBody()), + patch -> applyPatchForField(CassandraVacationTable.TEXT, patch.getTextBody()), + patch -> applyPatchForField(CassandraVacationTable.IS_ENABLED, patch.getIsEnabled()), + patch -> applyPatchForFieldZonedDateTime(CassandraVacationTable.FROM_DATE, patch.getFromDate()), + patch -> applyPatchForFieldZonedDateTime(CassandraVacationTable.TO_DATE, patch.getToDate())) + .stream() + .reduce(FunctionGenerator::composeGeneratedFunctions) + .get(); } - public CompletableFuture<Void> modifyVacation(AccountId accountId, Vacation vacation) { + public CompletableFuture<Void> modifyVacation(AccountId accountId, VacationPatch vacationPatch) { return cassandraAsyncExecutor.executeVoid( - insertStatement.bind() - .setString(CassandraVacationTable.ACCOUNT_ID, accountId.getIdentifier()) - .setBool(CassandraVacationTable.IS_ENABLED, vacation.isEnabled()) - .setUDTValue(CassandraVacationTable.FROM_DATE, convertToUDTValue(vacation.getFromDate())) - .setUDTValue(CassandraVacationTable.TO_DATE, convertToUDTValue(vacation.getToDate())) - .setString(CassandraVacationTable.TEXT, vacation.getTextBody().orElse(null)) - .setString(CassandraVacationTable.SUBJECT, vacation.getSubject().orElse(null)) - .setString(CassandraVacationTable.HTML, vacation.getHtmlBody().orElse(null))); + createSpecificUpdate(vacationPatch, + insertInto(CassandraVacationTable.TABLE_NAME) + .value(CassandraVacationTable.ACCOUNT_ID, accountId.getIdentifier()))); } public CompletableFuture<Optional<Vacation>> retrieveVacation(AccountId accountId) { @@ -104,11 +107,30 @@ public class CassandraVacationDAO { .getZonedDateTime()); } - private UDTValue convertToUDTValue(Optional<ZonedDateTime> zonedDateTimeOptional) { + private Insert createSpecificUpdate(VacationPatch vacationPatch, Insert baseInsert) { + return insertGeneratorPipeline + .apply(vacationPatch) + .apply(baseInsert); + } + + public <T> Function<Insert, Insert> applyPatchForField(String field, PatchedValue<T> patchedValue) { + return patchedValue.mapNotKeptToOptional(optionalValue -> applyPatchForField(field, optionalValue)) + .orElse(Function.identity()); + } + + public Function<Insert, Insert> applyPatchForFieldZonedDateTime(String field, PatchedValue<ZonedDateTime> patchedValue) { + return patchedValue.mapNotKeptToOptional(optionalValue -> applyPatchForField(field, convertToUDTOptional(optionalValue))) + .orElse(Function.identity()); + } + + private <T> Function<Insert, Insert> applyPatchForField(String field, Optional<T> value) { + return insert -> insert.value(field, value.orElse(null)); + } + + private Optional<UDTValue> convertToUDTOptional(Optional<ZonedDateTime> zonedDateTimeOptional) { return zonedDateTimeOptional.map(ZonedDateTimeRepresentation::fromZonedDateTime) .map(representation -> zonedDateTimeUserType.newValue() .setDate(CassandraZonedDateTimeModule.DATE, representation.getDate()) - .setString(CassandraZonedDateTimeModule.TIME_ZONE, representation.getSerializedZoneId())) - .orElse(null); + .setString(CassandraZonedDateTimeModule.TIME_ZONE, representation.getSerializedZoneId())); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/6da59847/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationRepository.java ---------------------------------------------------------------------- diff --git a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationRepository.java b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationRepository.java index 60958d4..51574f5 100644 --- a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationRepository.java +++ b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/vacation/CassandraVacationRepository.java @@ -26,6 +26,9 @@ import javax.inject.Inject; import org.apache.james.jmap.api.vacation.AccountId; import org.apache.james.jmap.api.vacation.Vacation; import org.apache.james.jmap.api.vacation.VacationRepository; +import org.apache.james.jmap.api.vacation.VacationPatch; + +import com.google.common.base.Preconditions; public class CassandraVacationRepository implements VacationRepository { @@ -37,8 +40,14 @@ public class CassandraVacationRepository implements VacationRepository { } @Override - public CompletableFuture<Void> modifyVacation(AccountId accountId, Vacation vacation) { - return cassandraVacationDAO.modifyVacation(accountId, vacation); + public CompletableFuture<Void> modifyVacation(AccountId accountId, VacationPatch vacationPatch) { + Preconditions.checkNotNull(accountId); + Preconditions.checkNotNull(vacationPatch); + if (vacationPatch.isIdentity()) { + return CompletableFuture.completedFuture(null); + } else { + return cassandraVacationDAO.modifyVacation(accountId, vacationPatch); + } } @Override --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
