Skip to content

Commit

Permalink
SDK-580: Return structured_postal_address when postal_address is missing
Browse files Browse the repository at this point in the history
  • Loading branch information
bucky-boy committed Oct 15, 2018
1 parent c34e903 commit 2d6d42b
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public static final class HumanProfileAttributes {
public static final String SELFIE = "selfie";
public static final String EMAIL_ADDRESS = "email_address";
public static final String DOCUMENT_DETAILS = "document_details";

public static final class Keys {

public static final String FORMATTED_ADDRESS = "formatted_address";
}

}

public static final class ApplicationProfileAttributes {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.yoti.api.client.spi.remote;

import java.util.Map;

import com.yoti.api.attributes.AttributeConstants.HumanProfileAttributes;
import com.yoti.api.client.Attribute;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class AddressTransformer {

private static final Logger LOG = LoggerFactory.getLogger(AttributeListConverter.class);

private AddressTransformer() {}

static final AddressTransformer newInstance() {
return new AddressTransformer();
}

public Attribute<String> transform(Attribute<?> structuredAddress) {
Attribute<String> transformedAddress = null;
try {
Attribute<Map<?, ?>> address = (Attribute<Map<?, ?>>) structuredAddress;
if (address.getValue() != null) {
Object formattedAddress = address.getValue().get(HumanProfileAttributes.Keys.FORMATTED_ADDRESS);
if (formattedAddress != null) {
transformedAddress = new SimpleAttribute(HumanProfileAttributes.POSTAL_ADDRESS,
String.valueOf(formattedAddress),
structuredAddress.getSources(),
structuredAddress.getVerifiers(),
structuredAddress.getAnchors());
}
}
} catch (Exception e) {
LOG.warn("Failed to transform attribute '{}' in place of missing '{}' due to '{}'", structuredAddress.getName(),
HumanProfileAttributes.POSTAL_ADDRESS, e.getMessage());
}
return transformedAddress;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.yoti.api.client.spi.remote;

import static com.yoti.api.attributes.AttributeConstants.HumanProfileAttributes.POSTAL_ADDRESS;
import static com.yoti.api.attributes.AttributeConstants.HumanProfileAttributes.STRUCTURED_POSTAL_ADDRESS;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
Expand All @@ -20,13 +23,15 @@ class AttributeListConverter {
private static final Logger LOG = LoggerFactory.getLogger(AttributeListConverter.class);

private final AttributeConverter attributeConverter;
private final AddressTransformer addressTransformer;

private AttributeListConverter(AttributeConverter attributeConverter) {
private AttributeListConverter(AttributeConverter attributeConverter, AddressTransformer addressTransformer) {
this.attributeConverter = attributeConverter;
this.addressTransformer = addressTransformer;
}

static AttributeListConverter newInstance() {
return new AttributeListConverter(AttributeConverter.newInstance());
return new AttributeListConverter(AttributeConverter.newInstance(), AddressTransformer.newInstance());
}

List<Attribute<?>> parseAttributeList(byte[] attributeListBytes) throws ProfileException {
Expand All @@ -37,6 +42,7 @@ List<Attribute<?>> parseAttributeList(byte[] attributeListBytes) throws ProfileE
AttributeListProto.AttributeList attributeList = parseProto(attributeListBytes);
List<Attribute<?>> attributes = parseAttributes(attributeList);
LOG.debug("{} out of {} attribute(s) parsed successfully ", attributes.size(), attributeList.getAttributesCount());
ensurePostalAddress(attributes);
return attributes;
}

Expand All @@ -54,10 +60,32 @@ private List<Attribute<?>> parseAttributes(AttributeListProto.AttributeList mess
try {
parsedAttributes.add(attributeConverter.convertAttribute(attribute));
} catch (IOException | ParseException e) {
LOG.warn("Failed to parse attribute '{}'", attribute.getName());
LOG.warn("Failed to parse attribute '{}' due to '{}'", attribute.getName(), e.getMessage());
}
}
return parsedAttributes;
}

private void ensurePostalAddress(List<Attribute<?>> attributes) {
if (findAttribute(POSTAL_ADDRESS, attributes) == null) {
Attribute<?> structuredAddress = findAttribute(STRUCTURED_POSTAL_ADDRESS, attributes);
if (structuredAddress != null) {
Attribute<String> transformedAddress = addressTransformer.transform(structuredAddress);
if (transformedAddress != null) {
LOG.debug("Substituting '{}' in place of missing '{}'", STRUCTURED_POSTAL_ADDRESS, POSTAL_ADDRESS);
attributes.add(transformedAddress);
}
}
}
}

private Attribute<?> findAttribute(String name, List<Attribute<?>> attributes) {
for (Attribute<?> attribute : attributes) {
if (name.equals(attribute.getName())) {
return attribute;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.yoti.api.client.spi.remote;

import static java.util.Arrays.asList;

import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;

import java.util.HashMap;
import java.util.Map;

import com.yoti.api.attributes.AttributeConstants.HumanProfileAttributes;
import com.yoti.api.client.Anchor;
import com.yoti.api.client.Attribute;

import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class AddressTransformerTest {

private static final String SOME_FORMATTED_ADDRESS = "someFormattedAddress";

@InjectMocks AddressTransformer testObj;

@Mock Attribute<Map> structuredAddressMock;
@Mock Attribute<Object> badAttributeMock;
@Mock Anchor sourceAnchorMock;
@Mock Anchor verifierAnchorMock;

@Test
public void shouldReturnNullWhenAddressValueIsNull() {
Attribute<String> result = testObj.transform(structuredAddressMock);

assertThat(result, is(nullValue()));
}

@Test
public void shouldReturnNullWhenFormattedAddressIsNull() {
when(structuredAddressMock.getValue()).thenReturn(new HashMap());

Attribute<String> result = testObj.transform(structuredAddressMock);

assertThat(result, is(nullValue()));
}

@Test
public void shouldReturnNullWhenTheresAnException() {
when(badAttributeMock.getValue()).thenReturn(new Object());

Attribute<String> result = testObj.transform(badAttributeMock);

assertThat(result, is(nullValue()));
}

@Test
public void shouldReturnTransformedAddress() {
Map<String, String> addressMap = new HashMap<>();
addressMap.put(HumanProfileAttributes.Keys.FORMATTED_ADDRESS, SOME_FORMATTED_ADDRESS);
when(structuredAddressMock.getValue()).thenReturn(addressMap);
when(structuredAddressMock.getSources()).thenReturn(asList(sourceAnchorMock));
when(structuredAddressMock.getVerifiers()).thenReturn(asList(verifierAnchorMock));
when(structuredAddressMock.getAnchors()).thenReturn(asList(sourceAnchorMock, verifierAnchorMock));

Attribute<String> result = testObj.transform(structuredAddressMock);

assertThat(result.getName(), Matchers.is(HumanProfileAttributes.POSTAL_ADDRESS));
assertThat(result.getValue(), is(SOME_FORMATTED_ADDRESS));
assertThat(result.getSources(), hasItems(sourceAnchorMock));
assertThat(result.getVerifiers(), hasItems(verifierAnchorMock));
assertThat(result.getAnchors(), hasItems(sourceAnchorMock, verifierAnchorMock));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.yoti.api.attributes.AttributeConstants;
import com.yoti.api.client.Attribute;
import com.yoti.api.client.Date;
import com.yoti.api.client.ProfileException;
import com.yoti.api.client.spi.remote.proto.AttrProto;
import com.yoti.api.client.spi.remote.proto.AttributeListProto;

import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
Expand All @@ -45,20 +49,31 @@ public class AttributeListConverterTest {
.setName(JSON_ATTRIBUTE_NAME)
.build();

private static final byte[] PROFILE_DATA = AttributeListProto.AttributeList.newBuilder()
.addAttributes(STRING_ATTRIBUTE_PROTO)
.addAttributes(DATE_ATTRIBUTE_PROTO)
.addAttributes(JSON_ATTRIBUTE_PROTO)
.build()
.toByteArray();
private static final AttrProto.Attribute POSTAL_ADDRESS_PROTO = AttrProto.Attribute.newBuilder()
.setName(AttributeConstants.HumanProfileAttributes.POSTAL_ADDRESS)
.build();

private static final AttrProto.Attribute STRUCTURED_ADDRESS_PROTO = AttrProto.Attribute.newBuilder()
.setName(AttributeConstants.HumanProfileAttributes.STRUCTURED_POSTAL_ADDRESS)
.build();

private static final byte[] PROFILE_DATA = createProfileData(STRING_ATTRIBUTE_PROTO, DATE_ATTRIBUTE_PROTO, JSON_ATTRIBUTE_PROTO);

@InjectMocks AttributeListConverter testObj;

@Mock AttributeConverter attributeConverterMock;
@Mock AddressTransformer addressTransformerMock;

@Mock Attribute<String> stringAttributeMock;
@Mock Attribute<Date> dateAttributeMock;
@Mock Attribute<Map> jsonAttributeMock;
@Mock Attribute<String> postalAddressMock;
@Mock Attribute<Map> structuredAddressMock;

@Before
public void setUp() throws Exception {
when(structuredAddressMock.getName()).thenReturn(AttributeConstants.HumanProfileAttributes.STRUCTURED_POSTAL_ADDRESS);
}

@Test
public void shouldReturnEmptyListForNullProfileData() throws Exception {
Expand Down Expand Up @@ -109,4 +124,57 @@ public void shouldTolerateFailureToParseSomeAttributes() throws Exception {
assertThat(result, hasItem(stringAttributeMock));
}

@Test
public void shouldNotSubstituteStructuredAddressWhenPostalAddressIsPresent() throws Exception {
when(attributeConverterMock.<String>convertAttribute(POSTAL_ADDRESS_PROTO)).thenReturn(postalAddressMock);

List<Attribute<?>> result = testObj.parseAttributeList(createProfileData(POSTAL_ADDRESS_PROTO));

assertThat(result, hasSize(1));
assertThat(result, hasItem(postalAddressMock));
verifyZeroInteractions(addressTransformerMock);
}

@Test
public void shouldNotSubstituteStructuredAddressWhenItsNotPresent() throws Exception {
when(attributeConverterMock.<String>convertAttribute(STRING_ATTRIBUTE_PROTO)).thenReturn(stringAttributeMock);
when(attributeConverterMock.<Date>convertAttribute(DATE_ATTRIBUTE_PROTO)).thenReturn(dateAttributeMock);
when(attributeConverterMock.<Map>convertAttribute(JSON_ATTRIBUTE_PROTO)).thenReturn(jsonAttributeMock);

List<Attribute<?>> result = testObj.parseAttributeList(PROFILE_DATA);

assertThat(result, hasSize(3));
assertThat(result, hasItems(stringAttributeMock, dateAttributeMock, jsonAttributeMock));
verifyZeroInteractions(addressTransformerMock);
}

@Test
public void shouldSubstituteStructuredAddressForMissingPostalAddress() throws Exception {
when(attributeConverterMock.<Map>convertAttribute(STRUCTURED_ADDRESS_PROTO)).thenReturn(structuredAddressMock);
when(addressTransformerMock.transform(structuredAddressMock)).thenReturn(postalAddressMock);

List<Attribute<?>> result = testObj.parseAttributeList(createProfileData(STRUCTURED_ADDRESS_PROTO));

assertThat(result, hasSize(2));
assertThat(result, hasItems(structuredAddressMock, postalAddressMock));
}

@Test
public void shouldNotAddNullTransformedAddress() throws Exception {
when(attributeConverterMock.<Map>convertAttribute(STRUCTURED_ADDRESS_PROTO)).thenReturn(structuredAddressMock);
when(addressTransformerMock.transform(structuredAddressMock)).thenReturn(null);

List<Attribute<?>> result = testObj.parseAttributeList(createProfileData(STRUCTURED_ADDRESS_PROTO));

assertThat(result, hasSize(1));
assertThat(result, hasItem(structuredAddressMock));
}

private static byte[] createProfileData(AttrProto.Attribute... attributes) {
return AttributeListProto.AttributeList.newBuilder()
.addAllAttributes(Arrays.asList(attributes))
.build()
.toByteArray();
}

}

0 comments on commit 2d6d42b

Please sign in to comment.