Skip to content

Commit 8018e56

Browse files
committed
[#2774] Fix updating of status properties
The MongoDbBasedTenantService has been changed to keep an instance's original creation time when updating the instance. Signed-off-by: Kai Hudalla <[email protected]>
1 parent 0eaddf9 commit 8018e56

File tree

2 files changed

+184
-7
lines changed

2 files changed

+184
-7
lines changed

Diff for: services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedTenantService.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,23 @@ private Future<OperationResult<Void>> modifyTenant(
208208
.withTenantId(tenantId)
209209
.document();
210210

211-
final Promise<JsonObject> updateTenantPromise = Promise.promise();
212-
final TenantDto newTenantDto = TenantDto.forUpdate(tenantId, newTenant, new Versioned<>(newTenant).getVersion());
213-
mongoClient.findOneAndReplaceWithOptions(config.getCollectionName(), updateTenantQuery,
214-
JsonObject.mapFrom(newTenantDto), new FindOptions(), new UpdateOptions().setReturningNewDocument(true),
215-
updateTenantPromise);
216-
217-
return updateTenantPromise.future()
211+
return findTenant(tenantId)
212+
.map(existingDto -> TenantDto.forUpdate(
213+
() -> existingDto,
214+
newTenant,
215+
new Versioned<>(newTenant).getVersion()))
216+
.compose(newTenantDto -> {
217+
final Promise<JsonObject> updateTenantPromise = Promise.promise();
218+
mongoClient.findOneAndReplaceWithOptions(
219+
config.getCollectionName(),
220+
updateTenantQuery,
221+
JsonObject.mapFrom(newTenantDto),
222+
new FindOptions(),
223+
new UpdateOptions().setReturningNewDocument(true),
224+
updateTenantPromise);
225+
226+
return updateTenantPromise.future();
227+
})
218228
.compose(updateResult -> Optional.ofNullable(updateResult)
219229
.map(updated -> {
220230
span.log("successfully updated tenant");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* Copyright (c) 2021 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
14+
15+
package org.eclipse.hono.deviceregistry.mongodb.service;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.ArgumentMatchers.anyString;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.verify;
23+
import static org.mockito.Mockito.when;
24+
25+
import java.time.Instant;
26+
import java.time.temporal.ChronoUnit;
27+
import java.util.Optional;
28+
29+
import org.eclipse.hono.deviceregistry.mongodb.config.MongoDbBasedTenantsConfigProperties;
30+
import org.eclipse.hono.deviceregistry.mongodb.model.TenantDto;
31+
import org.eclipse.hono.service.management.tenant.Tenant;
32+
import org.eclipse.hono.test.VertxMockSupport;
33+
import org.eclipse.hono.util.RegistryManagementConstants;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.api.extension.ExtendWith;
37+
import org.mockito.ArgumentCaptor;
38+
39+
import io.opentracing.noop.NoopSpan;
40+
import io.vertx.core.AsyncResult;
41+
import io.vertx.core.Future;
42+
import io.vertx.core.Handler;
43+
import io.vertx.core.Vertx;
44+
import io.vertx.core.json.JsonObject;
45+
import io.vertx.ext.mongo.FindOptions;
46+
import io.vertx.ext.mongo.MongoClient;
47+
import io.vertx.ext.mongo.UpdateOptions;
48+
import io.vertx.junit5.VertxExtension;
49+
import io.vertx.junit5.VertxTestContext;
50+
51+
52+
/**
53+
* Tests verifying {@link MongoDbBasedTenantService}'s management of status properties.
54+
*
55+
*/
56+
@ExtendWith(VertxExtension.class)
57+
public class MongoDbBasedTenantServiceStatusPropertiesTest {
58+
59+
private MongoClient mongoClient;
60+
private MongoDbBasedTenantService service;
61+
62+
/**
63+
* Sets up the fixture.
64+
*/
65+
@BeforeEach
66+
void setUp(final Vertx vertx) {
67+
final var config = new MongoDbBasedTenantsConfigProperties();
68+
mongoClient = mock(MongoClient.class);
69+
service = new MongoDbBasedTenantService(vertx, mongoClient, config);
70+
}
71+
72+
/**
73+
* Verifies that the service sets the initial version and creation date when creating a tenant.
74+
*
75+
* @param ctx The vert.x test context.
76+
*/
77+
@Test
78+
public void testCreateDeviceSetsCreationDate(final VertxTestContext ctx) {
79+
80+
when(mongoClient.insert(anyString(), any(JsonObject.class), VertxMockSupport.anyHandler()))
81+
.thenAnswer(invocation -> {
82+
final Handler<AsyncResult<String>> resultHandler = invocation.getArgument(2);
83+
resultHandler.handle(Future.succeededFuture("document_id"));
84+
return mongoClient;
85+
});
86+
87+
service.createTenant(Optional.of("tenantId"), new Tenant(), NoopSpan.INSTANCE)
88+
.onComplete(ctx.succeeding(createResult -> {
89+
ctx.verify(() -> {
90+
assertThat(createResult.getPayload().getId()).isEqualTo("tenantId");
91+
final var document = ArgumentCaptor.forClass(JsonObject.class);
92+
verify(mongoClient).insert(eq("tenants"), document.capture(), VertxMockSupport.anyHandler());
93+
assertThat(document.getValue().getString(TenantDto.FIELD_VERSION))
94+
.as("tenant document contains resource version")
95+
.isNotNull();
96+
assertThat(document.getValue().getInstant(RegistryManagementConstants.FIELD_STATUS_CREATION_DATE))
97+
.as("tenant document contains creation time")
98+
.isNotNull();
99+
assertThat(document.getValue().getInstant(TenantDto.FIELD_UPDATED_ON))
100+
.as("tenant document contains last update time")
101+
.isNull();
102+
});
103+
ctx.completeNow();
104+
}));
105+
}
106+
107+
/**
108+
* Verifies that the sets sets the new version and last update time but also keeps the original
109+
* creation date when updating a tenant.
110+
*
111+
* @param ctx The vert.x test context.
112+
*/
113+
@Test
114+
public void testUpdateSetsLastUpdate(final VertxTestContext ctx) {
115+
116+
final var existingRecord = TenantDto.forRead(
117+
"tenantId",
118+
new Tenant(),
119+
Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.SECONDS),
120+
null,
121+
"initial-version");
122+
123+
when(mongoClient.findOne(anyString(), any(), any(), VertxMockSupport.anyHandler()))
124+
.thenAnswer(invocation -> {
125+
final Handler<AsyncResult<JsonObject>> resultHandler = invocation.getArgument(3);
126+
resultHandler.handle(Future.succeededFuture(JsonObject.mapFrom(existingRecord)));
127+
return mongoClient;
128+
});
129+
130+
when(mongoClient.findOneAndReplaceWithOptions(
131+
anyString(),
132+
any(JsonObject.class),
133+
any(JsonObject.class),
134+
any(FindOptions.class),
135+
any(UpdateOptions.class),
136+
VertxMockSupport.anyHandler()))
137+
.thenAnswer(invocation -> {
138+
final Handler<AsyncResult<JsonObject>> resultHandler = invocation.getArgument(5);
139+
resultHandler.handle(Future.succeededFuture(new JsonObject().put(TenantDto.FIELD_VERSION, "new-version")));
140+
return mongoClient;
141+
});
142+
143+
service.updateTenant("tenantId", new Tenant(), Optional.empty(), NoopSpan.INSTANCE)
144+
.onComplete(ctx.succeeding(newVersion -> {
145+
ctx.verify(() -> {
146+
final var document = ArgumentCaptor.forClass(JsonObject.class);
147+
verify(mongoClient).findOneAndReplaceWithOptions(
148+
eq("tenants"),
149+
any(JsonObject.class),
150+
document.capture(),
151+
any(FindOptions.class),
152+
any(UpdateOptions.class),
153+
VertxMockSupport.anyHandler());
154+
assertThat(document.getValue().getString(TenantDto.FIELD_VERSION))
155+
.as("tenant document contains new resource version")
156+
.isNotEqualTo("initial-version");
157+
assertThat(document.getValue().getInstant(RegistryManagementConstants.FIELD_STATUS_CREATION_DATE))
158+
.as("tenant document contains original creation time")
159+
.isEqualTo(existingRecord.getCreationTime());
160+
assertThat(document.getValue().getInstant(TenantDto.FIELD_UPDATED_ON))
161+
.as("tenant document contains last update time")
162+
.isAfterOrEqualTo(existingRecord.getCreationTime());
163+
});
164+
ctx.completeNow();
165+
}));
166+
}
167+
}

0 commit comments

Comments
 (0)