Skip to content

Commit 3f499d2

Browse files
committed
Return audited immutable entities from save
Signed-off-by: Artur Kalimullin <kalimullin@gmail.com>
1 parent 6ac0c34 commit 3f499d2

5 files changed

Lines changed: 89 additions & 6 deletions

File tree

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* @author Jorge Rodriguez Martin
4141
* @author Carlos Espinaco
4242
* @author Emilien Bevierre
43+
* @author Artur Kalimullin
4344
* @since 3.0
4445
*/
4546
class CouchbaseTemplateSupport extends AbstractTemplateSupport implements ApplicationContextAware, TemplateSupport {
@@ -59,6 +60,7 @@ public CouchbaseDocument encodeEntity(final Object entityToEncode) {
5960
Object maybeNewEntity = maybeCallBeforeConvert(entityToEncode, "");
6061
final CouchbaseDocument converted = new CouchbaseDocument();
6162
converter.write(maybeNewEntity, converted);
63+
converted.setEntityToWrite(maybeNewEntity);
6264
maybeCallAfterConvert(entityToEncode, converted, "");
6365
maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted));
6466
return converted;
@@ -77,9 +79,10 @@ public <T> T decodeEntity(Object id, byte[] source, Long cas, Instant expiryTime
7779
}
7880

7981
@Override
80-
public <T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas,
81-
Object txResultHolder, CouchbaseResourceHolder holder) {
82-
return applyResultBase(entity, converted, id, cas, txResultHolder, holder);
82+
@SuppressWarnings("unchecked")
83+
public <T> T applyResult(T entity, CouchbaseDocument converted, Object id, long cas, Object txResultHolder,
84+
CouchbaseResourceHolder holder) {
85+
return (T) applyResultBase(converted.getEntityToWrite(), converted, id, cas, txResultHolder, holder);
8386
}
8487

8588
@Override

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @author Carlos Espinaco
4242
* @author Michael Reiche
4343
* @author Emilien Bevierre
44+
* @author Artur Kalimullin
4445
* @since 4.2
4546
*/
4647
class ReactiveCouchbaseTemplateSupport extends AbstractTemplateSupport
@@ -61,6 +62,7 @@ public Mono<CouchbaseDocument> encodeEntity(final Object entityToEncode) {
6162
.flatMap(entity -> maybeCallBeforeConvert(entity, "")).map(maybeNewEntity -> {
6263
final CouchbaseDocument converted = new CouchbaseDocument();
6364
converter.write(maybeNewEntity, converted);
65+
converted.setEntityToWrite(maybeNewEntity);
6466
return converted;
6567
}).flatMap(converted -> maybeCallAfterConvert(entityToEncode, converted, "").thenReturn(converted))
6668
.doOnNext(converted -> maybeEmitEvent(new BeforeSaveEvent<>(entityToEncode, converted)));
@@ -88,9 +90,11 @@ public <T> Mono<T> decodeEntity(Object id, byte[] source, Long cas, Instant expi
8890
}
8991

9092
@Override
91-
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas,
92-
Object txResultHolder, CouchbaseResourceHolder holder) {
93-
return Mono.fromSupplier(() -> applyResultBase(entity, converted, id, cas, txResultHolder, holder));
93+
@SuppressWarnings("unchecked")
94+
public <T> Mono<T> applyResult(T entity, CouchbaseDocument converted, Object id, Long cas, Object txResultHolder,
95+
CouchbaseResourceHolder holder) {
96+
return Mono.fromSupplier(
97+
() -> (T) applyResultBase(converted.getEntityToWrite(), converted, id, cas, txResultHolder, holder));
9498
}
9599

96100
@Override

src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseDocument.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* topmost document most likely has an ID.
3434
*
3535
* @author Michael Nitschinger
36+
* @author Artur Kalimullin
3637
*/
3738
public class CouchbaseDocument implements CouchbaseStorable {
3839

@@ -56,6 +57,11 @@ public class CouchbaseDocument implements CouchbaseStorable {
5657
*/
5758
private int expiration;
5859

60+
/**
61+
* Contains the entity encoded into this document.
62+
*/
63+
private Object entityToWrite;
64+
5965
/**
6066
* Creates a completely empty {@link CouchbaseDocument}.
6167
*/
@@ -260,6 +266,26 @@ public CouchbaseDocument setId(Object id) {
260266
return this;
261267
}
262268

269+
/**
270+
* Returns the entity encoded into this document.
271+
*
272+
* @return the entity encoded into this document.
273+
*/
274+
public Object getEntityToWrite() {
275+
return entityToWrite;
276+
}
277+
278+
/**
279+
* Sets the entity encoded into this document.
280+
*
281+
* @param entityToWrite the entity encoded into this document.
282+
* @return this document.
283+
*/
284+
public CouchbaseDocument setEntityToWrite(Object entityToWrite) {
285+
this.entityToWrite = entityToWrite;
286+
return this;
287+
}
288+
263289
/**
264290
* Verifies that only values of a certain and supported type can be stored.
265291
* <p>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.couchbase.domain;
17+
18+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
19+
20+
/**
21+
* @author Artur Kalimullin
22+
*/
23+
public interface ReactiveAuditedRecordRepository extends ReactiveCrudRepository<AuditedRecord, String> {}

src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryKeyValueIntegrationTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.springframework.data.couchbase.domain.LibraryRepository;
5454
import org.springframework.data.couchbase.domain.PersonValue;
5555
import org.springframework.data.couchbase.domain.PersonValueRepository;
56+
import org.springframework.data.couchbase.domain.ReactiveAuditedRecordRepository;
5657
import org.springframework.data.couchbase.domain.Submission;
5758
import org.springframework.data.couchbase.domain.SubscriptionToken;
5859
import org.springframework.data.couchbase.domain.SubscriptionTokenRepository;
@@ -89,6 +90,7 @@ public class CouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareInt
8990
@Autowired AirlineRepository airlineRepository;
9091
@Autowired AuditedImmutableEntityRepository auditedImmutableEntityRepository;
9192
@Autowired AuditedRecordRepository auditedRecordRepository;
93+
@Autowired ReactiveAuditedRecordRepository reactiveAuditedRecordRepository;
9294
@Autowired PersonValueRepository personValueRepository;
9395
@Autowired CouchbaseTemplate couchbaseTemplate;
9496
@Autowired AuditingDateTimeProvider auditingDateTimeProvider;
@@ -188,6 +190,8 @@ void saveAuditedRecord() {
188190

189191
assertNotNull(found.id());
190192
assertNotEquals(0, found.version());
193+
assertEquals(createdAt, saved.createdDate());
194+
assertEquals(createdAt, saved.lastModifiedDate());
191195
assertEquals(createdAt, found.createdDate());
192196
assertEquals(createdAt, found.lastModifiedDate());
193197

@@ -221,6 +225,8 @@ void saveAuditedImmutableEntity() {
221225
saved = auditedImmutableEntityRepository.save(new AuditedImmutableEntity(null, 0, null, null, "value"));
222226
AuditedImmutableEntity found = auditedImmutableEntityRepository.findById(saved.getId()).orElseThrow();
223227

228+
assertEquals(createdAt, saved.getCreatedDate());
229+
assertEquals(createdAt, saved.getLastModifiedDate());
224230
assertEquals(createdAt, found.getCreatedDate());
225231
assertEquals(createdAt, found.getLastModifiedDate());
226232

@@ -242,6 +248,27 @@ void saveAuditedImmutableEntity() {
242248
}
243249
}
244250

251+
@Test
252+
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
253+
void reactiveSaveAuditedRecord() {
254+
Instant createdAt = Instant.parse("2026-08-12T09:00:00Z");
255+
AuditedRecord saved = null;
256+
setAuditingTime(createdAt);
257+
258+
try {
259+
saved = reactiveAuditedRecordRepository.save(new AuditedRecord(null, 0, null, null, "value")).block();
260+
261+
assertNotNull(saved);
262+
assertEquals(createdAt, saved.createdDate());
263+
assertEquals(createdAt, saved.lastModifiedDate());
264+
} finally {
265+
resetAuditingTime();
266+
if (saved != null) {
267+
reactiveAuditedRecordRepository.deleteById(saved.id()).block();
268+
}
269+
}
270+
}
271+
245272
private void setAuditingTime(Instant time) {
246273
auditingDateTimeProvider.setDateTimeService(() -> ZonedDateTime.ofInstant(time, ZoneOffset.UTC));
247274
}

0 commit comments

Comments
 (0)