diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java index e602e3a3270..303c26a2884 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java @@ -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 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 diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidator.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidator.java deleted file mode 100644 index 577343a5502..00000000000 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidator.java +++ /dev/null @@ -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 getResponseEntity(IBase theAddress, FhirContext theFhirContext) throws Exception { - Map 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 getRequestParams(IBase theAddress) { - AddressHelper helper = new AddressHelper(null, theAddress); - - Map 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 addressErrors = new ArrayList<>(); - private List addressChange = new ArrayList<>(); - private List geocodeStatus = new ArrayList<>(); - private List geocodeError = new ArrayList<>(); - private List 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(""); - } - } -} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidatorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidatorTest.java index 6fdbd7777f8..9dfa0487ba2 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidatorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidatorTest.java @@ -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 diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidatorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidatorTest.java deleted file mode 100644 index 2b229860b74..00000000000 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/MelissaAddressValidatorTest.java +++ /dev/null @@ -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 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); - }); - } - -}