Updated standardization logic and removed obsolete validator

This commit is contained in:
Nick Goupinets 2021-04-26 16:27:44 -04:00
parent 9e153ddbcd
commit 08934d2364
4 changed files with 19 additions and 294 deletions

View File

@ -44,7 +44,6 @@ import org.springframework.http.ResponseEntity;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.util.Properties;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -65,7 +64,7 @@ public class LoquateAddressValidator extends BaseRestfulValidator {
protected static final String DEFAULT_DATA_CLEANSE_ENDPOINT = "https://api.addressy.com/Cleansing/International/Batch/v1.00/json4.ws";
protected static final int MAX_ADDRESS_LINES = 8;
private Pattern myPattern = Pattern.compile("\\,(\\S)");
private Pattern myCommaPattern = Pattern.compile("\\,(\\S)");
public LoquateAddressValidator(Properties theProperties) {
super(theProperties);
@ -128,7 +127,7 @@ public class LoquateAddressValidator extends BaseRestfulValidator {
IBase addressBase = theFhirContext.getElementDefinition("Address").newInstance();
AddressHelper helper = new AddressHelper(theFhirContext, addressBase);
helper.setText(getString(match, "Address"));
helper.setText(standardize(getString(match, "Address")));
String str = getString(match, "Address1");
if (str != null) {
@ -217,14 +216,22 @@ public class LoquateAddressValidator extends BaseRestfulValidator {
String text = theNode.get(theField).asText();
if (StringUtils.isEmpty(text)) {
return "";
}
return text;
}
Matcher m = myPattern.matcher(text);
if (m.find()) {
text = m.replaceAll(", $1");
protected String standardize(String theText) {
if (StringUtils.isEmpty(theText)) {
return "";
}
return text.trim();
theText = theText.replaceAll("\\s\\s", ", ");
Matcher m = myCommaPattern.matcher(theText);
if (m.find()) {
theText = m.replaceAll(", $1");
}
return theText.trim();
}
@Override

View File

@ -1,145 +0,0 @@
package ca.uhn.fhir.rest.server.interceptor.validation.address.impl;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.interceptor.validation.address.AddressValidationException;
import ca.uhn.fhir.rest.server.interceptor.validation.address.AddressValidationResult;
import ca.uhn.fhir.rest.server.interceptor.validation.helpers.AddressHelper;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.springframework.http.ResponseEntity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
public class MelissaAddressValidator extends BaseRestfulValidator {
public static final String GLOBAL_ADDRESS_VALIDATION_ENDPOINT = "https://address.melissadata.net/v3/WEB/GlobalAddress/doGlobalAddress" +
"?id={id}&a1={a1}&a2={a2}&ctry={ctry}&format={format}";
public MelissaAddressValidator(Properties theProperties) {
super(theProperties);
}
@Override
protected AddressValidationResult getValidationResult(AddressValidationResult theResult, JsonNode theResponse, FhirContext theFhirContext) {
Response response = new Response(theResponse);
theResult.setValid(response.isValidAddress());
theResult.setValidatedAddressString(response.getAddress());
return theResult;
}
@Override
protected ResponseEntity<String> getResponseEntity(IBase theAddress, FhirContext theFhirContext) throws Exception {
Map<String, String> requestParams = getRequestParams(theAddress);
return newTemplate().getForEntity(getApiEndpoint(), String.class, requestParams);
}
@Override
protected String getApiEndpoint() {
String endpoint = super.getApiEndpoint();
return StringUtils.isEmpty(endpoint) ? GLOBAL_ADDRESS_VALIDATION_ENDPOINT : endpoint;
}
protected Map<String, String> getRequestParams(IBase theAddress) {
AddressHelper helper = new AddressHelper(null, theAddress);
Map<String, String> requestParams = new HashMap<>();
requestParams.put("t", UUID.randomUUID().toString());
requestParams.put("id", getApiKey());
requestParams.put("a1", helper.getLine());
requestParams.put("a2", helper.getParts());
requestParams.put("ctry", helper.getCountry());
requestParams.put("format", "json");
return requestParams;
}
private static class Response {
private JsonNode root;
private JsonNode records;
private JsonNode results;
private List<String> addressErrors = new ArrayList<>();
private List<String> addressChange = new ArrayList<>();
private List<String> geocodeStatus = new ArrayList<>();
private List<String> geocodeError = new ArrayList<>();
private List<String> addressVerification = new ArrayList<>();
public Response(JsonNode theRoot) {
root = theRoot;
// see codes here - http://wiki.melissadata.com/index.php?title=Result_Codes
String transmissionResults = root.get("TransmissionResults").asText();
if (!StringUtils.isEmpty(transmissionResults)) {
geocodeError.add(transmissionResults);
throw new AddressValidationException(String.format("Transmission result %s indicate an error with the request - please check API_KEY", transmissionResults));
}
int recordCount = root.get("TotalRecords").asInt();
if (recordCount < 1) {
throw new AddressValidationException("Expected at least one record in the address validation response");
}
// get first match
records = root.get("Records").get(0);
results = records.get("Results");
// full list of response codes is available here
// http://wiki.melissadata.com/index.php?title=Result_Code_Details#Global_Address_Verification
for (String s : results.asText().split(",")) {
if (s.startsWith("AE")) {
addressErrors.add(s);
} else if (s.startsWith("AC")) {
addressChange.add(s);
} else if (s.startsWith("GS")) {
geocodeStatus.add(s);
} else if (s.startsWith("GE")) {
geocodeError.add(s);
} else if (s.startsWith("AV")) {
addressVerification.add(s);
}
}
}
public boolean isValidAddress() {
if (!geocodeError.isEmpty()) {
return false;
}
return addressErrors.isEmpty() && (geocodeStatus.contains("GS05") || geocodeStatus.contains("GS06"));
}
public String getAddress() {
if (records == null) {
return "";
}
if (!records.has("FormattedAddress")) {
return "";
}
return records.get("FormattedAddress").asText("");
}
}
}

View File

@ -125,9 +125,11 @@ class LoquateAddressValidatorTest {
ObjectNode node = new ObjectNode(null, new HashMap<>());
node.set("text1", new TextNode("This,Is,Text"));
node.set("text2", new TextNode("This Is-Text,"));
node.set("text3", new TextNode("This Is-Text with Invalid Formatting"));
assertEquals("This, Is, Text", myValidator.getString(node, "text1"));
assertEquals("This Is-Text,", myValidator.getString(node, "text2"));
assertEquals("This, Is, Text", myValidator.standardize(myValidator.getString(node, "text1")));
assertEquals("This Is-Text,", myValidator.standardize(myValidator.getString(node, "text2")));
assertEquals("This Is-Text, with Invalid Formatting", myValidator.standardize(myValidator.getString(node, "text3")));
}
@Test

View File

@ -1,139 +0,0 @@
package ca.uhn.fhir.rest.server.interceptor.validation.address.impl;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.interceptor.validation.address.AddressValidationException;
import ca.uhn.fhir.rest.server.interceptor.validation.address.AddressValidationResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hl7.fhir.r4.model.Address;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
class MelissaAddressValidatorTest {
private static final String RESPONSE_INVALID_ADDRESS = "{\n" +
" \"Version\": \"3.0.1.160\",\n" +
" \"TransmissionReference\": \"1\",\n" +
" \"TransmissionResults\": \"\",\n" +
" \"TotalRecords\": \"1\",\n" +
" \"Records\": [\n" +
" {\n" +
" \"RecordID\": \"1\",\n" +
" \"Results\": \"AC01,AC12,AE02,AV12,GE02\",\n" +
" \"FormattedAddress\": \"100 Main Street\",\n" +
" \"Organization\": \"\",\n" +
" \"AddressLine1\": \"100 Main Street\"\n" +
" }\n" +
" ]\n" +
"}";
private static final String RESPONSE_VALID_ADDRESS = "{\n" +
" \"Version\": \"3.0.1.160\",\n" +
" \"TransmissionReference\": \"1\",\n" +
" \"TransmissionResults\": \"\",\n" +
" \"TotalRecords\": \"1\",\n" +
" \"Records\": [\n" +
" {\n" +
" \"RecordID\": \"1\",\n" +
" \"Results\": \"AC01,AV24,GS05\",\n" +
" \"FormattedAddress\": \"100 Main St W;Hamilton ON L8P 1H6\"\n" +
" }\n" +
" ]\n" +
"}";
private static final String RESPONSE_INVALID_KEY = "{\n" +
" \"Version\": \"3.0.1.160\",\n" +
" \"TransmissionReference\": \"1\",\n" +
" \"TransmissionResults\": \"GE05\",\n" +
" \"TotalRecords\": \"0\"\n" +
"}";
private static FhirContext ourContext = FhirContext.forR4();
private MelissaAddressValidator myValidator;
@BeforeEach
public void init() {
Properties props = new Properties();
props.setProperty(MelissaAddressValidator.PROPERTY_SERVICE_KEY, "MY_KEY");
myValidator = new MelissaAddressValidator(props);
}
@Test
public void testRequestBody() {
Map<String, String> params = myValidator.getRequestParams(getAddress());
assertEquals("Line 1, Line 2", params.get("a1"));
assertEquals("City, POSTAL", params.get("a2"));
assertEquals("Country", params.get("ctry"));
assertEquals("MY_KEY", params.get("id"));
assertEquals("json", params.get("format"));
assertTrue(params.containsKey("t"));
}
@Test
public void testServiceCalled() {
Address address = getAddress();
final RestTemplate template = mock(RestTemplate.class);
Properties props = new Properties();
props.setProperty(BaseRestfulValidator.PROPERTY_SERVICE_KEY, "MY_KEY");
MelissaAddressValidator val = new MelissaAddressValidator(props) {
@Override
protected RestTemplate newTemplate() {
return template;
}
};
try {
val.getResponseEntity(address, ourContext);
} catch (Exception e) {
fail();
}
verify(template, times(1)).getForEntity(any(String.class), eq(String.class), any(Map.class));
}
private Address getAddress() {
Address address = new Address();
address.addLine("Line 1").addLine("Line 2").setCity("City").setPostalCode("POSTAL").setCountry("Country");
return address;
}
@Test
public void testSuccessfulResponses() throws Exception {
AddressValidationResult res = myValidator.getValidationResult(new AddressValidationResult(),
new ObjectMapper().readTree(RESPONSE_INVALID_ADDRESS), ourContext);
assertFalse(res.isValid());
res = myValidator.getValidationResult(new AddressValidationResult(),
new ObjectMapper().readTree(RESPONSE_VALID_ADDRESS), ourContext);
assertTrue(res.isValid());
assertEquals("100 Main St W;Hamilton ON L8P 1H6", res.getValidatedAddressString());
}
@Test
public void testErrorResponses() throws Exception {
assertThrows(AddressValidationException.class, () -> {
myValidator.getValidationResult(new AddressValidationResult(),
new ObjectMapper().readTree(RESPONSE_INVALID_KEY), ourContext);
});
}
}