Skip to content

Commit 82b6d4b

Browse files
committed
DATACOUCH-631 - Add expiry option to replace.
1 parent 8c38f2d commit 82b6d4b

File tree

5 files changed

+106
-23
lines changed

5 files changed

+106
-23
lines changed

src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18+
import java.time.Duration;
1819
import java.util.Collection;
1920

2021
import com.couchbase.client.core.msg.kv.DurabilityLevel;
@@ -46,6 +47,11 @@ interface ReplaceByIdWithDurability<T> extends ReplaceByIdWithCollection<T> {
4647

4748
}
4849

49-
interface ExecutableReplaceById<T> extends ReplaceByIdWithDurability<T> {}
50+
interface ReplaceByIdWithExpiry<T> extends ReplaceByIdWithDurability<T> {
51+
52+
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
53+
}
54+
55+
interface ExecutableReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
5056

5157
}

src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperationSupport.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18+
import java.time.Duration;
1819
import java.util.Collection;
1920

2021
import org.springframework.util.Assert;
@@ -35,7 +36,7 @@ public ExecutableReplaceByIdOperationSupport(final CouchbaseTemplate template) {
3536
public <T> ExecutableReplaceById<T> replaceById(final Class<T> domainType) {
3637
Assert.notNull(domainType, "DomainType must not be null!");
3738
return new ExecutableReplaceByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
38-
DurabilityLevel.NONE);
39+
DurabilityLevel.NONE, Duration.ZERO);
3940
}
4041

4142
static class ExecutableReplaceByIdSupport<T> implements ExecutableReplaceById<T> {
@@ -46,18 +47,21 @@ static class ExecutableReplaceByIdSupport<T> implements ExecutableReplaceById<T>
4647
private final PersistTo persistTo;
4748
private final ReplicateTo replicateTo;
4849
private final DurabilityLevel durabilityLevel;
50+
private final Duration expiry;
4951
private final ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport<T> reactiveSupport;
5052

5153
ExecutableReplaceByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String collection,
52-
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel) {
54+
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel,
55+
final Duration expiry) {
5356
this.template = template;
5457
this.domainType = domainType;
5558
this.collection = collection;
5659
this.persistTo = persistTo;
5760
this.replicateTo = replicateTo;
5861
this.durabilityLevel = durabilityLevel;
62+
this.expiry = expiry;
5963
this.reactiveSupport = new ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport<>(template.reactive(),
60-
domainType, collection, persistTo, replicateTo, durabilityLevel);
64+
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry);
6165
}
6266

6367
@Override
@@ -74,22 +78,29 @@ public Collection<? extends T> all(Collection<? extends T> objects) {
7478
public TerminatingReplaceById<T> inCollection(final String collection) {
7579
Assert.hasText(collection, "Collection must not be null nor empty.");
7680
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
77-
durabilityLevel);
81+
durabilityLevel, expiry);
7882
}
7983

8084
@Override
8185
public ReplaceByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
8286
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
8387
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
84-
durabilityLevel);
88+
durabilityLevel, expiry);
8589
}
8690

8791
@Override
8892
public ReplaceByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
8993
Assert.notNull(persistTo, "PersistTo must not be null.");
9094
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
9195
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
92-
durabilityLevel);
96+
durabilityLevel, expiry);
97+
}
98+
99+
@Override
100+
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
101+
Assert.notNull(expiry, "expiry must not be null.");
102+
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
103+
durabilityLevel, expiry);
93104
}
94105

95106
}

src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21+
import java.time.Duration;
2122
import java.util.Collection;
2223

2324
import com.couchbase.client.core.msg.kv.DurabilityLevel;
@@ -49,6 +50,11 @@ interface ReplaceByIdWithDurability<T> extends ReplaceByIdWithCollection<T> {
4950

5051
}
5152

52-
interface ReactiveReplaceById<T> extends ReplaceByIdWithDurability<T> {}
53+
interface ReplaceByIdWithExpiry<T> extends ReplaceByIdWithDurability<T> {
54+
55+
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
56+
}
57+
58+
interface ReactiveReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
5359

5460
}

src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21+
import java.time.Duration;
2122
import java.util.Collection;
2223

2324
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
25+
import org.springframework.data.couchbase.core.mapping.Document;
2426
import org.springframework.util.Assert;
2527

2628
import com.couchbase.client.core.msg.kv.DurabilityLevel;
@@ -40,7 +42,7 @@ public ReactiveReplaceByIdOperationSupport(final ReactiveCouchbaseTemplate templ
4042
public <T> ReactiveReplaceById<T> replaceById(final Class<T> domainType) {
4143
Assert.notNull(domainType, "DomainType must not be null!");
4244
return new ReactiveReplaceByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
43-
DurabilityLevel.NONE);
45+
DurabilityLevel.NONE, Duration.ZERO);
4446
}
4547

4648
static class ReactiveReplaceByIdSupport<T> implements ReactiveReplaceById<T> {
@@ -51,16 +53,18 @@ static class ReactiveReplaceByIdSupport<T> implements ReactiveReplaceById<T> {
5153
private final PersistTo persistTo;
5254
private final ReplicateTo replicateTo;
5355
private final DurabilityLevel durabilityLevel;
56+
private final Duration expiry;
5457

5558
ReactiveReplaceByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType,
5659
final String collection, final PersistTo persistTo, final ReplicateTo replicateTo,
57-
final DurabilityLevel durabilityLevel) {
60+
final DurabilityLevel durabilityLevel, final Duration expiry) {
5861
this.template = template;
5962
this.domainType = domainType;
6063
this.collection = collection;
6164
this.persistTo = persistTo;
6265
this.replicateTo = replicateTo;
6366
this.durabilityLevel = durabilityLevel;
67+
this.expiry = expiry;
6468
}
6569

6670
@Override
@@ -93,6 +97,13 @@ private ReplaceOptions buildReplaceOptions(T object) {
9397
} else if (durabilityLevel != DurabilityLevel.NONE) {
9498
options.durability(durabilityLevel);
9599
}
100+
if (expiry != null && !expiry.isZero()) {
101+
options.expiry(expiry);
102+
} else if (domainType.isAnnotationPresent(Document.class)) {
103+
Document documentAnn = domainType.getAnnotation(Document.class);
104+
long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry());
105+
options.expiry(Duration.ofSeconds(durationSeconds));
106+
}
96107
long cas = template.support().getCas(object);
97108
options.cas(cas);
98109
return options;
@@ -101,23 +112,30 @@ private ReplaceOptions buildReplaceOptions(T object) {
101112
@Override
102113
public TerminatingReplaceById<T> inCollection(final String collection) {
103114
Assert.hasText(collection, "Collection must not be null nor empty.");
104-
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
105-
durabilityLevel);
115+
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
116+
expiry);
106117
}
107118

108119
@Override
109120
public ReplaceByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
110121
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
111-
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
112-
durabilityLevel);
122+
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
123+
expiry);
113124
}
114125

115126
@Override
116127
public ReplaceByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
117128
Assert.notNull(persistTo, "PersistTo must not be null.");
118129
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
119-
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
120-
durabilityLevel);
130+
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
131+
expiry);
132+
}
133+
134+
@Override
135+
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
136+
Assert.notNull(expiry, "expiry must not be null.");
137+
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
138+
expiry);
121139
}
122140

123141
}

src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,25 @@
1616

1717
package org.springframework.data.couchbase.core;
1818

19-
import static org.junit.jupiter.api.Assertions.*;
20-
import static org.springframework.data.couchbase.config.BeanNames.*;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
import static org.junit.jupiter.api.Assertions.assertTrue;
24+
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
25+
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
2126

2227
import java.io.IOException;
2328
import java.time.Duration;
2429
import java.util.UUID;
2530

26-
import com.couchbase.client.core.error.DocumentNotFoundException;
27-
import com.couchbase.client.java.kv.PersistTo;
28-
import com.couchbase.client.java.kv.ReplicateTo;
2931
import org.junit.jupiter.api.AfterAll;
3032
import org.junit.jupiter.api.BeforeAll;
3133
import org.junit.jupiter.api.BeforeEach;
3234
import org.junit.jupiter.api.Test;
3335
import org.springframework.context.ApplicationContext;
3436
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
35-
import org.springframework.dao.DataRetrievalFailureException;
36-
import org.springframework.dao.DataIntegrityViolationException;;
37+
import org.springframework.dao.DataIntegrityViolationException;
3738
import org.springframework.dao.DataRetrievalFailureException;
3839
import org.springframework.dao.DuplicateKeyException;
3940
import org.springframework.data.couchbase.CouchbaseClientFactory;
@@ -45,6 +46,9 @@
4546
import org.springframework.data.couchbase.util.ClusterType;
4647
import org.springframework.data.couchbase.util.IgnoreWhen;
4748

49+
import com.couchbase.client.java.kv.PersistTo;
50+
import com.couchbase.client.java.kv.ReplicateTo;
51+
4852
/**
4953
* KV tests Theses tests rely on a cb server running.
5054
*
@@ -142,6 +146,44 @@ void upsertWithExpiryAnnotation() {
142146
}
143147
}
144148

149+
@Test
150+
void replaceWithExpiry() {
151+
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
152+
try {
153+
User modified = couchbaseTemplate.upsertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user);
154+
couchbaseTemplate.replaceById(User.class).withExpiry(Duration.ofSeconds(1)).one(user);
155+
assertEquals(user, modified);
156+
sleepSecs(2);
157+
User found = couchbaseTemplate.findById(User.class).one(user.getId());
158+
assertNull(found, "found should have been null as document should be expired");
159+
} finally {
160+
try {
161+
couchbaseTemplate.removeById().one(user.getId());
162+
} catch (DataRetrievalFailureException e) {
163+
//
164+
}
165+
}
166+
}
167+
168+
@Test
169+
void replaceWithExpiryAnnotation() {
170+
UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname");
171+
try {
172+
UserAnnotated modified = couchbaseTemplate.upsertById(UserAnnotated.class).one(user);
173+
modified = couchbaseTemplate.replaceById(UserAnnotated.class).one(user);
174+
assertEquals(user, modified);
175+
sleepSecs(6);
176+
User found = couchbaseTemplate.findById(UserAnnotated.class).one(user.getId());
177+
assertNull(found, "found should have been null as document should be expired");
178+
} finally {
179+
try {
180+
couchbaseTemplate.removeById().one(user.getId());
181+
} catch (DataRetrievalFailureException e) {
182+
//
183+
}
184+
}
185+
}
186+
145187
@Test
146188
void findDocWhichDoesNotExist() {
147189
assertNull(couchbaseTemplate.findById(User.class).one(UUID.randomUUID().toString()));

0 commit comments

Comments
 (0)