Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Introduce StructureValueAccessor#getFieldAccessor. #560

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
/**
* Interface for value accessors allowed create and get value of attribute
*
* @param <InputT> input type
* @param <OutputT> output type
* @param <InputT> Type of "raw" input element, that we want to convert into normalized form.
* @param <OutputT> Normalized "output" type, eg. `bytes -> string` for schema type `string`.
*/
@Experimental
public interface AttributeValueAccessor<InputT, OutputT> extends Serializable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,28 @@ static StructureValue of(Map<String, Object> value) {

public interface StructureValueAccessor<T> extends AttributeValueAccessor<T, StructureValue> {

/**
* 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 <OutputT> Type of raw value. This is only to simplify casting of returned value.
* @return Raw value.
*/
<OutputT> OutputT getRawFieldValue(String name, T structure);

@Override
default Type getType() {
return Type.STRUCTURE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ public void testArrayWithStructureValue() {
private static class TestStructureAccessor
implements StructureValueAccessor<Map<String, Object>> {

@Override
public AttributeValueAccessor<?, ?> getFieldAccessor(String name) {
throw new UnsupportedOperationException("Not implemented.");
}

@Override
public <OutputT> OutputT getRawFieldValue(String name, Map<String, Object> structure) {
throw new UnsupportedOperationException("Not implemented.");
}

@Override
public StructureValue valueOf(Map<String, Object> object) {
return StructureValue.of(object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ public ProtoMessageValueAccessor(Factory<T> defaultValueFactory) {
}));
}

@Override
public AttributeValueAccessor<?, ?> getFieldAccessor(String name) {
return fieldAccessors.get(name);
}

@Override
public <OutputT> 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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: This should be probably IlegalArgumentException

String.format("Field %s not found in %s.", name, structure.getClass()));
}

@Override
public StructureValue valueOf(T object) {
return StructureValue.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<RuleConfig> accessor =
new ProtoMessageValueAccessor<>(RuleConfig::getDefaultInstance);
Expand All @@ -237,4 +239,44 @@ public void testCreateProtoWhereRepeatedFieldChangedToOptional() {
assertArrayEquals(
"second".getBytes(StandardCharsets.UTF_8), created.getPayload().toByteArray());
}

@Test
public void testFieldAccessors() {
final StructureValueAccessor<ValueSchemeMessage> accessor =
new ProtoMessageValueAccessor<>(ValueSchemeMessage::getDefaultInstance);
final InnerMessage innerMessage =
InnerMessage.newBuilder().setInnerEnum(Directions.LEFT).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<InnerMessage> innerMessageAccessor =
(StructureValueAccessor<InnerMessage>) 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")
final AttributeValueAccessors.PrimitiveValueAccessor<String, String> stringTypeAccessor =
(AttributeValueAccessors.PrimitiveValueAccessor<String, String>)
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));
}
}
2 changes: 1 addition & 1 deletion scheme/proto/src/test/resources/test-proto.conf
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
attributes: [ "*" ]
storage: "inmem:///data/proxima/gateway"
type: replica
access: [ commit-log, random-access ]
access: [ random-access ]
}
dummy-storage: {
entity: dummy
Expand Down