Skip to content

Commit 777df10

Browse files
Extract notification methods in identified object store (#77)
* extract notification methods * refactor * address review comments * address review comments * update DefaultObjectStore and ContextuallyIdentifiedObjectStore * address review comments * addres review comments * address review comments * remove unused object * address review comments * add tests
1 parent b99c3ad commit 777df10

File tree

6 files changed

+195
-36
lines changed

6 files changed

+195
-36
lines changed

object-store/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
api(libs.hypertrace.grpcutils.context)
1111

1212
implementation(projects.configServiceChangeEventGenerator)
13+
implementation(libs.slf4j.api)
1314

1415
annotationProcessor(libs.lombok)
1516
compileOnly(libs.lombok)

object-store/src/main/java/org/hypertrace/config/objectstore/ContextuallyIdentifiedObjectStore.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ protected ContextuallyIdentifiedObjectStore(
3838

3939
protected abstract Value buildValueFromData(T data);
4040

41+
protected Value buildValueForChangeEvent(T data) {
42+
return this.buildValueFromData(data);
43+
}
44+
45+
protected String buildClassNameForChangeEvent(T data) {
46+
return data.getClass().getName();
47+
}
48+
4149
protected abstract String getConfigContextFromRequestContext(RequestContext requestContext);
4250

4351
private IdentifiedObjectStore<T> buildObjectStoreForContext(RequestContext context) {
@@ -86,6 +94,16 @@ protected Value buildValueFromData(T data) {
8694
return ContextuallyIdentifiedObjectStore.this.buildValueFromData(data);
8795
}
8896

97+
@Override
98+
protected Value buildValueForChangeEvent(T data) {
99+
return ContextuallyIdentifiedObjectStore.this.buildValueForChangeEvent(data);
100+
}
101+
102+
@Override
103+
protected String buildClassNameForChangeEvent(T data) {
104+
return ContextuallyIdentifiedObjectStore.this.buildClassNameForChangeEvent(data);
105+
}
106+
89107
@Override
90108
protected String getContextFromData(T data) {
91109
return ContextuallyIdentifiedObjectStore.this.getConfigContextFromRequestContext(

object-store/src/main/java/org/hypertrace/config/objectstore/DefaultObjectStore.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.protobuf.Value;
44
import io.grpc.Status;
55
import java.util.Optional;
6+
import lombok.extern.slf4j.Slf4j;
67
import org.hypertrace.config.service.change.event.api.ConfigChangeEventGenerator;
78
import org.hypertrace.config.service.v1.ConfigServiceGrpc.ConfigServiceBlockingStub;
89
import org.hypertrace.config.service.v1.ContextSpecificConfig;
@@ -18,6 +19,7 @@
1819
*
1920
* @param <T>
2021
*/
22+
@Slf4j
2123
public abstract class DefaultObjectStore<T> {
2224
private final ConfigServiceBlockingStub configServiceBlockingStub;
2325
private final String resourceNamespace;
@@ -49,6 +51,14 @@ protected DefaultObjectStore(
4951

5052
protected abstract Value buildValueFromData(T data);
5153

54+
protected Value buildValueForChangeEvent(T data) {
55+
return this.buildValueFromData(data);
56+
}
57+
58+
protected String buildClassNameForChangeEvent(T data) {
59+
return data.getClass().getName();
60+
}
61+
5262
public Optional<T> getData(RequestContext context) {
5363
try {
5464
Value value =
@@ -91,12 +101,22 @@ public ConfigObject<T> upsertObject(RequestContext context, T data) {
91101
if (response.hasPrevConfig()) {
92102
configChangeEventGenerator.sendUpdateNotification(
93103
context,
94-
upsertedObject.getData().getClass().getName(),
95-
response.getPrevConfig(),
96-
response.getConfig());
104+
this.buildClassNameForChangeEvent(upsertedObject.getData()),
105+
this.buildDataFromValue(response.getPrevConfig())
106+
.map(this::buildValueForChangeEvent)
107+
.orElseGet(
108+
() -> {
109+
log.error(
110+
"Unable to convert previousValue back to data for change event. Falling back to raw value {}",
111+
response.getPrevConfig());
112+
return response.getPrevConfig();
113+
}),
114+
this.buildValueForChangeEvent(upsertedObject.getData()));
97115
} else {
98116
configChangeEventGenerator.sendCreateNotification(
99-
context, upsertedObject.getData().getClass().getName(), response.getConfig());
117+
context,
118+
this.buildClassNameForChangeEvent(upsertedObject.getData()),
119+
this.buildValueForChangeEvent(upsertedObject.getData()));
100120
}
101121
});
102122
return upsertedObject;
@@ -120,7 +140,9 @@ public Optional<ConfigObject<T>> deleteObject(RequestContext context) {
120140
configChangeEventGeneratorOptional.ifPresent(
121141
configChangeEventGenerator ->
122142
configChangeEventGenerator.sendDeleteNotification(
123-
context, object.getData().getClass().getName(), deletedConfig.getConfig()));
143+
context,
144+
this.buildClassNameForChangeEvent(object.getData()),
145+
this.buildValueForChangeEvent(object.getData())));
124146
return Optional.of(object);
125147
} catch (Exception exception) {
126148
if (Status.fromThrowable(exception).equals(Status.NOT_FOUND)) {

object-store/src/main/java/org/hypertrace/config/objectstore/IdentifiedObjectStore.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66
import java.util.Optional;
77
import java.util.stream.Collectors;
8+
import lombok.extern.slf4j.Slf4j;
89
import org.hypertrace.config.service.change.event.api.ConfigChangeEventGenerator;
910
import org.hypertrace.config.service.v1.ConfigServiceGrpc.ConfigServiceBlockingStub;
1011
import org.hypertrace.config.service.v1.ContextSpecificConfig;
@@ -24,6 +25,7 @@
2425
*
2526
* @param <T>
2627
*/
28+
@Slf4j
2729
public abstract class IdentifiedObjectStore<T> {
2830
private final ConfigServiceBlockingStub configServiceBlockingStub;
2931
private final String resourceNamespace;
@@ -57,6 +59,14 @@ protected IdentifiedObjectStore(
5759

5860
protected abstract String getContextFromData(T data);
5961

62+
protected Value buildValueForChangeEvent(T data) {
63+
return this.buildValueFromData(data);
64+
}
65+
66+
protected String buildClassNameForChangeEvent(T data) {
67+
return data.getClass().getName();
68+
}
69+
6070
protected List<ContextualConfigObject<T>> orderFetchedObjects(
6171
List<ContextualConfigObject<T>> objects) {
6272
return objects;
@@ -141,7 +151,10 @@ public Optional<ContextualConfigObject<T>> deleteObject(RequestContext context,
141151
configChangeEventGeneratorOptional.ifPresent(
142152
configChangeEventGenerator ->
143153
configChangeEventGenerator.sendDeleteNotification(
144-
context, object.getData().getClass().getName(), id, deletedConfig.getConfig()));
154+
context,
155+
this.buildClassNameForChangeEvent(object.getData()),
156+
id,
157+
this.buildValueForChangeEvent(object.getData())));
145158
return Optional.of(object);
146159
} catch (Exception exception) {
147160
if (Status.fromThrowable(exception).equals(Status.NOT_FOUND)) {
@@ -181,6 +194,8 @@ private Optional<ContextualConfigObject<T>> processUpsertResult(
181194
Optional<ContextualConfigObject<T>> optionalResult =
182195
ContextualConfigObjectImpl.tryBuild(
183196
response, this::buildDataFromValue, this::getContextFromData);
197+
198+
System.out.println(optionalResult);
184199
optionalResult.ifPresent(
185200
result -> {
186201
if (response.hasPrevConfig()) {
@@ -213,9 +228,9 @@ private void tryReportCreation(RequestContext requestContext, ContextualConfigOb
213228
configChangeEventGenerator ->
214229
configChangeEventGenerator.sendCreateNotification(
215230
requestContext,
216-
result.getData().getClass().getName(),
231+
this.buildClassNameForChangeEvent(result.getData()),
217232
result.getContext(),
218-
this.buildValueFromData(result.getData())));
233+
this.buildValueForChangeEvent(result.getData())));
219234
}
220235

221236
private void tryReportUpdate(
@@ -224,9 +239,17 @@ private void tryReportUpdate(
224239
configChangeEventGenerator ->
225240
configChangeEventGenerator.sendUpdateNotification(
226241
requestContext,
227-
result.getData().getClass().getName(),
242+
this.buildClassNameForChangeEvent(result.getData()),
228243
result.getContext(),
229-
previousValue,
230-
this.buildValueFromData(result.getData())));
244+
this.buildDataFromValue(previousValue)
245+
.map(this::buildValueForChangeEvent)
246+
.orElseGet(
247+
() -> {
248+
log.error(
249+
"Unable to convert previousValue back to data for change event. Falling back to raw value {}",
250+
previousValue);
251+
return previousValue;
252+
}),
253+
this.buildValueForChangeEvent(result.getData())));
231254
}
232255
}

object-store/src/test/java/org/hypertrace/config/objectstore/DefaultObjectStoreTest.java

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.eq;
56
import static org.mockito.Mockito.mock;
67
import static org.mockito.Mockito.times;
78
import static org.mockito.Mockito.verify;
89
import static org.mockito.Mockito.when;
910

11+
import com.google.protobuf.Struct;
1012
import com.google.protobuf.Value;
1113
import com.google.protobuf.util.Values;
1214
import io.grpc.Status;
@@ -43,7 +45,7 @@ class DefaultObjectStoreTest {
4345
@Mock(answer = Answers.CALLS_REAL_METHODS)
4446
RequestContext mockRequestContext;
4547

46-
DefaultObjectStore<TestObject> store;
48+
DefaultObjectStore<TestInternalObject> store;
4749

4850
@BeforeEach
4951
void beforeEach() {
@@ -56,7 +58,8 @@ void generatesConfigReadRequestForGet() {
5658
when(this.mockStub.getConfig(any()))
5759
.thenReturn(GetConfigResponse.newBuilder().setConfig(Values.of("test")).build());
5860

59-
assertEquals(Optional.of(new TestObject("test")), this.store.getData(this.mockRequestContext));
61+
assertEquals(
62+
Optional.of(new TestInternalObject("test")), this.store.getData(this.mockRequestContext));
6063

6164
verify(this.mockStub, times(1))
6265
.getConfig(
@@ -91,7 +94,7 @@ void generatesConfigDeleteRequest() {
9194
assertEquals(
9295
Optional.of(
9396
new ConfigObjectImpl<>(
94-
new TestObject("test"), TEST_CREATE_TIMESTAMP, TEST_UPDATE_TIMESTAMP)),
97+
new TestInternalObject("test"), TEST_CREATE_TIMESTAMP, TEST_UPDATE_TIMESTAMP)),
9598
this.store.deleteObject(mockRequestContext));
9699

97100
verify(this.mockStub)
@@ -104,6 +107,15 @@ void generatesConfigDeleteRequest() {
104107
when(this.mockStub.deleteConfig(any())).thenThrow(Status.NOT_FOUND.asRuntimeException());
105108

106109
assertEquals(Optional.empty(), this.store.deleteObject(mockRequestContext));
110+
111+
verify(this.configChangeEventGenerator, times(1))
112+
.sendDeleteNotification(
113+
eq(this.mockRequestContext),
114+
eq(TestApiObject.class.getName()),
115+
eq(
116+
Value.newBuilder()
117+
.setStructValue(Struct.newBuilder().putFields("api_name", Values.of("test")))
118+
.build()));
107119
}
108120

109121
@Test
@@ -115,38 +127,65 @@ void generatesConfigUpsertRequest() {
115127
.setUpdateTimestamp(TEST_UPDATE_TIMESTAMP.toEpochMilli())
116128
.setConfig(Values.of("updated"))
117129
.build());
118-
assertEquals(
130+
ConfigObject configObject =
119131
new ConfigObjectImpl<>(
120-
new TestObject("updated"), TEST_CREATE_TIMESTAMP, TEST_UPDATE_TIMESTAMP),
121-
this.store.upsertObject(this.mockRequestContext, new TestObject("updated")));
132+
new TestInternalObject("updated"), TEST_CREATE_TIMESTAMP, TEST_UPDATE_TIMESTAMP);
133+
assertEquals(
134+
configObject,
135+
this.store.upsertObject(this.mockRequestContext, new TestInternalObject("updated")));
122136
verify(this.mockStub, times(1))
123137
.upsertConfig(
124138
UpsertConfigRequest.newBuilder()
125139
.setResourceName(TEST_RESOURCE_NAME)
126140
.setResourceNamespace(TEST_RESOURCE_NAMESPACE)
127141
.setConfig(Values.of("updated"))
128142
.build());
143+
verify(this.configChangeEventGenerator, times(1))
144+
.sendCreateNotification(
145+
eq(this.mockRequestContext),
146+
eq(TestApiObject.class.getName()),
147+
eq(
148+
Value.newBuilder()
149+
.setStructValue(Struct.newBuilder().putFields("api_name", Values.of("updated")))
150+
.build()));
129151
}
130152

131153
@lombok.Value
132-
private static class TestObject {
154+
private static class TestInternalObject {
133155
String name;
134156
}
135157

136-
private static class TestObjectStore extends DefaultObjectStore<TestObject> {
158+
@lombok.Value
159+
private static class TestApiObject {
160+
String api_name;
161+
}
162+
163+
private static class TestObjectStore extends DefaultObjectStore<TestInternalObject> {
137164
private TestObjectStore(
138165
ConfigServiceBlockingStub stub, ConfigChangeEventGenerator configChangeEventGenerator) {
139166
super(stub, TEST_RESOURCE_NAMESPACE, TEST_RESOURCE_NAME, configChangeEventGenerator);
140167
}
141168

142169
@Override
143-
protected Optional<TestObject> buildDataFromValue(Value value) {
144-
return Optional.of(new TestObject(value.getStringValue()));
170+
protected Optional<TestInternalObject> buildDataFromValue(Value value) {
171+
return Optional.of(new TestInternalObject(value.getStringValue()));
145172
}
146173

147174
@Override
148-
protected Value buildValueFromData(TestObject object) {
175+
protected Value buildValueFromData(TestInternalObject object) {
149176
return Values.of(object.getName());
150177
}
178+
179+
@Override
180+
protected Value buildValueForChangeEvent(TestInternalObject object) {
181+
return Value.newBuilder()
182+
.setStructValue(Struct.newBuilder().putFields("api_name", Values.of(object.getName())))
183+
.build();
184+
}
185+
186+
@Override
187+
protected String buildClassNameForChangeEvent(TestInternalObject object) {
188+
return TestApiObject.class.getName();
189+
}
151190
}
152191
}

0 commit comments

Comments
 (0)