From 76200bd6d99f741ef4d6edceac4eda4d4107c250 Mon Sep 17 00:00:00 2001 From: David Moravek Date: Tue, 8 Jun 2021 15:01:58 +0200 Subject: [PATCH 1/2] [core] Introduce StructureValueAccessor#getFieldAccessor. --- .../scheme/AttributeValueAccessor.java | 4 +- .../scheme/AttributeValueAccessors.java | 22 ++++++++++ .../scheme/AttributeValueAccessorsTest.java | 10 +++++ .../proto/ProtoMessageValueAccessor.java | 25 +++++++++++ .../proto/ProtoMessageValueAccessorTest.java | 42 ++++++++++++++++++- .../proto/src/test/resources/test-proto.conf | 2 +- 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessor.java b/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessor.java index 0cdb14980..1ecc85a12 100644 --- a/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessor.java +++ b/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessor.java @@ -21,8 +21,8 @@ /** * Interface for value accessors allowed create and get value of attribute * - * @param input type - * @param output type + * @param Type of "raw" input element, that we want to convert into normalized form. + * @param Normalized "output" type, eg. `bytes -> string` for schema type `string`. */ @Experimental public interface AttributeValueAccessor extends Serializable { diff --git a/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessors.java b/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessors.java index 511a9f831..bf52868d2 100644 --- a/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessors.java +++ b/core/src/main/java/cz/o2/proxima/scheme/AttributeValueAccessors.java @@ -157,6 +157,28 @@ static StructureValue of(Map value) { public interface StructureValueAccessor extends AttributeValueAccessor { + /** + * Get accessor for a given field. Please not that accessors only work on "raw values". See + * {@link #getRawFieldValue(String, Object)} for more details. + * + * @param name Name of the field to get accessor for. + * @return Field accessor. + */ + AttributeValueAccessor getFieldAccessor(String name); + + /** + * Get raw value of a given field. In this context, raw value means a value before applying a + * field accessor on it (for example it can be a byte representation, that would be converted to + * a string after "accessing"). This is intended for partial message parsing and to be used in + * combination with {@link #getFieldAccessor(String)}. + * + * @param name Name of the field. + * @param structure Structure to get a raw value from. + * @param Type of raw value. This is only to simplify casting of returned value. + * @return Raw value. + */ + OutputT getRawFieldValue(String name, T structure); + @Override default Type getType() { return Type.STRUCTURE; diff --git a/core/src/test/java/cz/o2/proxima/scheme/AttributeValueAccessorsTest.java b/core/src/test/java/cz/o2/proxima/scheme/AttributeValueAccessorsTest.java index 45a39701b..49c5a3701 100644 --- a/core/src/test/java/cz/o2/proxima/scheme/AttributeValueAccessorsTest.java +++ b/core/src/test/java/cz/o2/proxima/scheme/AttributeValueAccessorsTest.java @@ -98,6 +98,16 @@ public void testArrayWithStructureValue() { private static class TestStructureAccessor implements StructureValueAccessor> { + @Override + public AttributeValueAccessor getFieldAccessor(String name) { + throw new UnsupportedOperationException("Not implemented."); + } + + @Override + public OutputT getRawFieldValue(String name, Map structure) { + throw new UnsupportedOperationException("Not implemented."); + } + @Override public StructureValue valueOf(Map object) { return StructureValue.of(object); diff --git a/scheme/proto/src/main/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessor.java b/scheme/proto/src/main/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessor.java index 0fa961aa4..2e480b7ef 100644 --- a/scheme/proto/src/main/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessor.java +++ b/scheme/proto/src/main/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessor.java @@ -69,6 +69,31 @@ public ProtoMessageValueAccessor(Factory defaultValueFactory) { })); } + @Override + public AttributeValueAccessor getFieldAccessor(String name) { + return fieldAccessors.get(name); + } + + @Override + public OutputT getRawFieldValue(String name, T structure) { + for (FieldDescriptor field : structure.getDescriptorForType().getFields()) { + if (name.equals(field.getName())) { + final Object rawValue = structure.getField(field); + if (rawValue instanceof EnumValueDescriptor) { + final EnumValueDescriptor cast = (EnumValueDescriptor) rawValue; + @SuppressWarnings("unchecked") + final OutputT result = (OutputT) cast.getName(); + return result; + } + @SuppressWarnings("unchecked") + final OutputT result = (OutputT) rawValue; + return result; + } + } + throw new IllegalStateException( + String.format("Field %s not found in %s.", name, structure.getClass())); + } + @Override public StructureValue valueOf(T object) { return StructureValue.of( diff --git a/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java b/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java index 48bdd8062..d9705c68d 100644 --- a/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java +++ b/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java @@ -18,9 +18,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import com.google.protobuf.ByteString; +import cz.o2.proxima.scheme.AttributeValueAccessors; import cz.o2.proxima.scheme.AttributeValueAccessors.StructureValue; import cz.o2.proxima.scheme.AttributeValueAccessors.StructureValueAccessor; import cz.o2.proxima.scheme.proto.test.Scheme.Event; @@ -219,7 +221,7 @@ public void testCreateProtoWhereOptionalFieldChangedToRepeated() { @Test public void testCreateProtoWhereRepeatedFieldChangedToOptional() { - // This situation can be simulate by creating object from list where last value should win. + // This situation can be simulated by creating object from list where last value should win. final StructureValueAccessor accessor = new ProtoMessageValueAccessor<>(RuleConfig::getDefaultInstance); @@ -237,4 +239,42 @@ public void testCreateProtoWhereRepeatedFieldChangedToOptional() { assertArrayEquals( "second".getBytes(StandardCharsets.UTF_8), created.getPayload().toByteArray()); } + + @Test + public void testFieldAccessors() { + final StructureValueAccessor accessor = + new ProtoMessageValueAccessor<>(ValueSchemeMessage::getDefaultInstance); + final InnerMessage innerMessage = InnerMessage.newBuilder().setInnerDoubleType(1.2).build(); + final ValueSchemeMessage message = + ValueSchemeMessage.newBuilder() + .setStringType("test string") + .setInnerMessage(innerMessage) + .build(); + + // Test getting "raw field value". This means that inner message doesn't get converted into map. + assertEquals("test string", accessor.getRawFieldValue("string_type", message)); + assertEquals(innerMessage, accessor.getRawFieldValue("inner_message", message)); + + // Test inner message accessor. + @SuppressWarnings("unchecked") + final StructureValueAccessor innerMessageAccessor = + (StructureValueAccessor) accessor.getFieldAccessor("inner_message"); + assertEquals( + 1.2d, innerMessageAccessor.getRawFieldValue("inner_double_type", innerMessage), 0.0); + + // Test string type accessor. + @SuppressWarnings("unchecked") + final AttributeValueAccessors.PrimitiveValueAccessor stringTypeAccessor = + (AttributeValueAccessors.PrimitiveValueAccessor) + accessor.getFieldAccessor("string_type"); + assertEquals( + "test string", + stringTypeAccessor.valueOf(accessor.getRawFieldValue("string_type", message))); + + // Test accessing unknown field. + assertThrows(IllegalStateException.class, () -> accessor.getRawFieldValue("unknown", message)); + + // Test defaults. + assertEquals(0L, (long) accessor.getRawFieldValue("long_type", message)); + } } diff --git a/scheme/proto/src/test/resources/test-proto.conf b/scheme/proto/src/test/resources/test-proto.conf index 27fd902f2..b8c221b7b 100644 --- a/scheme/proto/src/test/resources/test-proto.conf +++ b/scheme/proto/src/test/resources/test-proto.conf @@ -66,7 +66,7 @@ attributes: [ "*" ] storage: "inmem:///data/proxima/gateway" type: replica - access: [ commit-log, random-access ] + access: [ random-access ] } dummy-storage: { entity: dummy From 262fee70eaf08a1fcecb040c9443576739629efd Mon Sep 17 00:00:00 2001 From: David Moravek Date: Tue, 8 Jun 2021 15:57:53 +0200 Subject: [PATCH 2/2] [core] Sonar - increase coverage. --- .../proxima/scheme/proto/ProtoMessageValueAccessorTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java b/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java index d9705c68d..3ab7b2fbc 100644 --- a/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java +++ b/scheme/proto/src/test/java/cz/o2/proxima/scheme/proto/ProtoMessageValueAccessorTest.java @@ -244,7 +244,8 @@ public void testCreateProtoWhereRepeatedFieldChangedToOptional() { public void testFieldAccessors() { final StructureValueAccessor accessor = new ProtoMessageValueAccessor<>(ValueSchemeMessage::getDefaultInstance); - final InnerMessage innerMessage = InnerMessage.newBuilder().setInnerDoubleType(1.2).build(); + final InnerMessage innerMessage = + InnerMessage.newBuilder().setInnerEnum(Directions.LEFT).setInnerDoubleType(1.2).build(); final ValueSchemeMessage message = ValueSchemeMessage.newBuilder() .setStringType("test string") @@ -261,6 +262,7 @@ public void testFieldAccessors() { (StructureValueAccessor) accessor.getFieldAccessor("inner_message"); assertEquals( 1.2d, innerMessageAccessor.getRawFieldValue("inner_double_type", innerMessage), 0.0); + assertEquals("LEFT", innerMessageAccessor.getRawFieldValue("inner_enum", innerMessage)); // Test string type accessor. @SuppressWarnings("unchecked")