From 75e1fae6fe0e4c5f3e1a0afa8e937666bd706b50 Mon Sep 17 00:00:00 2001 From: David Moravek Date: Tue, 8 Jun 2021 15:01:58 +0200 Subject: [PATCH] [core] Introduce StructureValueAccessor#getFieldAccessor. --- .../scheme/AttributeValueAccessor.java | 4 +- .../scheme/AttributeValueAccessors.java | 22 ++++++++++ .../scheme/AttributeValueAccessorsTest.java | 10 +++++ .../proto/ProtoMessageValueAccessor.java | 25 ++++++++++++ .../proto/ProtoMessageValueAccessorTest.java | 40 +++++++++++++++++++ .../proto/src/test/resources/test-proto.conf | 2 +- 6 files changed, 100 insertions(+), 3 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 b57c947dd..8c9a2eb03 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 @@ -17,9 +17,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +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; @@ -229,4 +231,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