diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java index d98e1a1a0..3b5cbb8ff 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java @@ -1,6 +1,7 @@ package edu.suffolk.litlab.efsp.docassemble; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getBoolMember; +import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getNonEmptyStringMember; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getStringMember; import com.fasterxml.jackson.databind.JsonNode; @@ -42,34 +43,21 @@ public static Result fromNode( return Result.err(err); } - List phones = new ArrayList(); - if (node.has("mobile_number") && node.get("mobile_number").isTextual()) { - String mobile = node.get("mobile_number").asText(); - if (!mobile.isBlank()) { - phones.add(mobile); - } - } else { - InterviewVariable var = - collector.requestVar("mobile_number", "A mobile or cell phone number", "text"); - collector.addOptional(var); - } + List inputPhones = new ArrayList(); + var maybeMobile = getNonEmptyStringMember(node, "mobile_number"); + maybeMobile.ifPresent(number -> inputPhones.add(number)); - if (node.has("phone_number")) { - if (node.get("phone_number").isTextual()) { - String phone = node.get("phone_number").asText(); - if (!phone.isBlank()) { - phones.add(phone); - } - } else { - FilingError err = - FilingError.malformedInterview( - "phone number needs to be text: " + node.get("phone_number").toPrettyString()); - return Result.err(err); - } + var maybePhone = getNonEmptyStringMember(node, "phone_number"); + maybePhone.ifPresent(number -> inputPhones.add(number)); + + var phonesRes = parser.vetPhoneNumbers(inputPhones); + List phones; + if (phonesRes.isErr()) { + var phoneBuilder = collector.varBuilder().name("phone_number").datatype("text"); + collector.addTextError(phonesRes.expectErr(""), phoneBuilder); + phones = List.of(); } else { - InterviewVariable var = - collector.requestVar("phone_number", "A mobile or cell phone number", "text"); - collector.addOptional(var); + phones = phonesRes.expect(""); } Optional
addr = Optional.empty(); @@ -85,7 +73,14 @@ public static Result fromNode( } } } - Optional email = getStringMember(node, "email"); + var emailRes = parser.vetEmail(getStringMember(node, "email")); + Optional email; + if (emailRes.isErr()) { + collector.addTextError(emailRes.expectErr(""), collector.varBuilder().name("email")); + email = Optional.empty(); + } else { + email = emailRes.expect(""); + } final ContactInformation info = new ContactInformation(phones, addr, email); JsonNode partyJson = node.get("party_type"); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java index 3560d7c54..0a74e459d 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java @@ -82,6 +82,10 @@ public Result, CodeError> vetDamageAmount( public Result, CodeError> vetProcedureRemedy( Optional maybeProRem, boolean initial, CaseCategory cat); + public Result, TextVarError> vetEmail(Optional email); + + public Result, TextVarError> vetPhoneNumbers(List numbers); + public Result vetFirstName(Optional name); public Result vetMiddleName(Optional name); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializer.java index a5708096e..89d0d1700 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializer.java @@ -352,68 +352,22 @@ public ContactInformationType serializeEcfContactInformation( cit.getContactMeans().add(contactMeans); } - DataFieldRow phoneRow = allDataFields.getFieldRow("PartyPhone"); - if (phoneRow.isvisible) { - List numbers = contactInfo.getPhoneNumbers(); - InterviewVariable var = - collector.requestVar( - "phone_number", - "Phone number with regex: " + phoneRow.regularexpression, - "text", - List.of(), - Optional.of(numbers.toString())); - if (phoneRow.isrequired && numbers.isEmpty()) { - collector.addRequired(var); - } - boolean atLeastOnePhoneAdded = false; - for (String phoneNumber : numbers) { - if (!phoneRow.matchRegex(phoneNumber)) { - if (phoneNumber.contains("-") - || phoneNumber.contains("(") - || phoneNumber.contains(")") - || phoneNumber.contains(" ")) { - // HACK(brycew): Massachusetts doesn't like dashes in the number, just numbers - phoneNumber = - phoneNumber - .replace("-", "") - .replace("(", "") - .replace(")", "") - .strip() - .replace(" ", ""); - } - if (!phoneRow.matchRegex(phoneNumber)) { - continue; - } - } - - TelephoneNumberType tnt = niemObjFac.createTelephoneNumberType(); - FullTelephoneNumberType ftnt = niemObjFac.createFullTelephoneNumberType(); - ftnt.setTelephoneNumberFullID(Ecf4Helper.convertString(phoneNumber)); - tnt.setTelephoneNumberRepresentation(niemObjFac.createFullTelephoneNumber(ftnt)); - cit.getContactMeans().add(niemObjFac.createContactTelephoneNumber(tnt)); - atLeastOnePhoneAdded = true; - } - if (!numbers.isEmpty() && !atLeastOnePhoneAdded) { - collector.addWrong(var); - } + List numbers = contactInfo.getPhoneNumbers(); + for (String phoneNumber : numbers) { + TelephoneNumberType tnt = niemObjFac.createTelephoneNumberType(); + FullTelephoneNumberType ftnt = niemObjFac.createFullTelephoneNumberType(); + ftnt.setTelephoneNumberFullID(Ecf4Helper.convertString(phoneNumber)); + tnt.setTelephoneNumberRepresentation(niemObjFac.createFullTelephoneNumber(ftnt)); + cit.getContactMeans().add(niemObjFac.createContactTelephoneNumber(tnt)); } - DataFieldRow emailRow = allDataFields.getFieldRow("PartyEmail"); - if (emailRow.isvisible) { - InterviewVariable var = - collector.requestVar("email", "Email with regex " + emailRow.regularexpression, "email"); - if (contactInfo.getEmail().isPresent()) { - String em = contactInfo.getEmail().get(); - if (emailRow.matchRegex(em)) { - cit.getContactMeans().add(niemObjFac.createContactEmailID(Ecf4Helper.convertString(em))); - } else { - collector.addWrong(var); - } - } - if (emailRow.isrequired) { - collector.addRequired(var); - } - } + contactInfo + .getEmail() + .ifPresent( + email -> { + cit.getContactMeans() + .add(niemObjFac.createContactEmailID(Ecf4Helper.convertString(email))); + }); return cit; } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParser.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParser.java index 87f1aeb50..a8c126cab 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParser.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParser.java @@ -474,6 +474,77 @@ public Result, CodeError> vetDamageAmount( } } + /** + * Throws if something is wrong; otherwise, an optional email (empty does not mean error!). + * + *

Note that the collector type is what determines if it throws. + * + * @param email + * @param collector + * @return + */ + public Result, TextVarError> vetEmail(Optional email) { + DataFieldRow emailRow = allDataFields.getFieldRow("PartyEmail"); + if (emailRow.isvisible) { + if (email.isPresent()) { + if (!emailRow.matchRegex(email.get())) { + return Result.err(new WrongVar(email.get(), emailRow.regularexpression)); + } + } else if (emailRow.isrequired) { + return Result.err(new MissingVar(emailRow.regularexpression)); + } + } + return Result.ok(email); + } + + public Result, TextVarError> vetPhoneNumbers(List numbers) { + DataFieldRow phoneRow = allDataFields.getFieldRow("PartyPhone"); + if (phoneRow.isvisible) { + if (phoneRow.isrequired && numbers.isEmpty()) { + return Result.err(new MissingVar(phoneRow.regularexpression)); + } + if (numbers.isEmpty()) { + // if just visible but not required, keep the empty list + return Result.ok(List.of()); + } + var correctNumbers = + numbers.stream() + .map( + number -> { + if (phoneRow.matchRegex(number)) { + return number; + } + // HACK(brycew): Massachusetts doesn't like dashes in the number, just wants + // numbers + if (number.contains("+")) { + // there should be a space between the country code and the rest of the + // number, but no where else?? + // TODO(brycew): really just needs to be checked in docassemble itself + return number.replace("-", "").replace("(", "").replace(")", "").strip(); + } else { + return number + .replace("-", "") + .replace("(", "") + .replace(")", "") + .replace(" ", ""); + } + }) + .filter( + number -> { + if (number.isEmpty()) { + return false; + } + return phoneRow.matchRegex(number); + }) + .toList(); + if (correctNumbers.isEmpty()) { + return Result.err(new WrongVar(numbers.toString(), phoneRow.regularexpression)); + } + return Result.ok(correctNumbers); + } + return Result.ok(List.of()); + } + /** * Doesn't return optional because we can set the People names to null / blank. * diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/DocassembleToFilingInformationConverterTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/DocassembleToFilingInformationConverterTest.java index 32e62e080..4aa0db1e1 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/DocassembleToFilingInformationConverterTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/DocassembleToFilingInformationConverterTest.java @@ -74,13 +74,13 @@ public void setUp() throws IOException { List.of( Map.of( "PartyFirstName", - new DataFieldRow("PartyFirstName", "first name", true, true, "adams"), + new DataFieldRow("PartyFirstName", "first name", true, true, "adams"), "PartyMiddleName", - new DataFieldRow( - "PartyMiddleName", "middle name", true, false, "adams"), + new DataFieldRow("PartyMiddleName", "middle name", true, false, "adams"), "PartyLastName", - new DataFieldRow( - "PartyLastName", "last name", true, false, "adams"))))); + new DataFieldRow("PartyLastName", "last name", true, false, "adams"), + "PartyPhone", + new DataFieldRow("PartyPhone", "", true, false, "adams"))))); var loc = new CourtLocationInfo("01"); loc.initial = true; loc.subsequent = true; diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializerTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializerTest.java index 6f443eb4f..3b48afbbb 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializerTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializerTest.java @@ -21,8 +21,6 @@ import edu.suffolk.litlab.efsp.utils.InfoCollector; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,24 +28,26 @@ public class PersonDocassembleJacksonDeserializerTest { InfoCollector collector; CodesParser parser; + DataFields allDataFields; @BeforeEach public void setUp() { collector = new FailFastCollector(); var cd = mock(CodeDatabase.class); - when(cd.getDataFields("adams")) - .thenReturn( - new DataFields( - List.of( - Map.of( - "PartyFirstName", - new DataFieldRow("PartyFirstName", "first name", true, true, "adams"), - "PartyMiddleName", - new DataFieldRow( - "PartyMiddleName", "middle name", true, false, "adams"), - "PartyLastName", - new DataFieldRow( - "PartyLastName", "last name", true, false, "adams"))))); + var allDataFields = mock(DataFields.class); + when(cd.getDataFields("adams")).thenReturn(allDataFields); + when(allDataFields.getFieldRow("PartyFirstName")) + .thenReturn(new DataFieldRow("PartyFirstName", "", true, true, "adams")); + when(allDataFields.getFieldRow("PartyMiddleName")) + .thenReturn(new DataFieldRow("PartyMiddleName", "", true, false, "adams")); + when(allDataFields.getFieldRow("PartyLastName")) + .thenReturn(new DataFieldRow("PartyLastName", "", true, false, "adams")); + when(allDataFields.getFieldRow("PartyPhone")) + .thenReturn(new DataFieldRow("PartyPhone", "", true, false, "adams")); + when(allDataFields.getFieldRow("PartyEmail")) + .thenReturn(new DataFieldRow("PartyEmail", "", true, false, "adams")); + when(allDataFields.getFieldRow("PartyNameSuffix")) + .thenReturn(new DataFieldRow("PartyNameSuffix", "", false, false, "adams")); parser = new TylerCodesParser(cd, new CourtLocationInfo("adams")); } @@ -58,7 +58,7 @@ public void testShouldParseWithAllJsonFields() throws FilingError, IOException { assertThat(res.isOk()).isTrue(); var per = res.unwrapOrElseThrow(); assertThat(per.getContactInfo().getEmail()).contains("john.brown@example.com"); - assertThat(per.getContactInfo().getPhoneNumbers()).hasSize(1); + assertThat(per.getContactInfo().getPhoneNumbers()).hasSize(2); assertThat(per.getContactInfo().getAddress()) .isPresent(); // contains(new Address("123 Fake St", "", "Chicago", "IL", "60007", assertThat(per.getContactInfo().getAddress().get().getCity()).isEqualTo("Chicago"); diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializerTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializerTest.java index 46b07f673..30e541e82 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializerTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCourtSpecificSerializerTest.java @@ -183,7 +183,7 @@ public void shouldAllowAtLeastOnePhone() throws FilingError { collector = new AllWrongCollector(); ContactInformation info = new ContactInformation( - List.of("1234567890", "123-456-7890 ", "123-abc", "(123) 456-7890"), + List.of("1234567890", "(123) 456-7890"), Optional.empty(), Optional.of("bob@example.com")); CourtLocationInfo loc = new CourtLocationInfo("not_real"); diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParserTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParserTest.java index e1fe64c21..5367f8495 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParserTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/ecf4/tyler/TylerCodesParserTest.java @@ -545,5 +545,48 @@ public void testProcedureRemedyType() { var res = parser.vetProcedureRemedy(Optional.of("2244"), true, exampleCatProcRem); assertThat(res).containsOk(Optional.of(procRem)); } + + @Nested + class PhoneNumberTests { + @BeforeEach + public void setup() { + when(dataFields.getFieldRow("PartyPhone")) + .thenReturn( + new DataFieldRow( + "PartyPhone", + "phone number", + true, + false, + "", + "", + "", + "", + "^(\\+0?1\\s)?\\(?\\d{3}\\)?\\d{3}\\d{4}$", + null, + false, + "01")); + } + + @Test + public void testNormalPhoneNumber() { + var phones = List.of("4092345678"); + var res = parser.vetPhoneNumbers(phones); + assertThat(res).containsOk(phones); + } + + @Test + public void testBadPhoneNumber() { + var phones = List.of("+34092345678"); + var res = parser.vetPhoneNumbers(phones); + assertThat(res).isErr(); + } + + @Test + public void testOneBadOneOkayPhoneNumber() { + var phones = List.of("+34092345678", "+1 4092345678"); + var res = parser.vetPhoneNumbers(phones); + assertThat(res).containsOk(List.of("+1 4092345678")); + } + } } } diff --git a/proxyserver/src/test/resources/people/person_with_all.json b/proxyserver/src/test/resources/people/person_with_all.json index 748ceae05..59eb6fc48 100644 --- a/proxyserver/src/test/resources/people/person_with_all.json +++ b/proxyserver/src/test/resources/people/person_with_all.json @@ -16,7 +16,7 @@ "is_new": false, "is_form_filler": true, "phone_number": "+16171230000", - "mobile_number`": "+16171230000", + "mobile_number": "+16171230000", "email": "john.brown@example.com", "preferred_language": "en", "gender": "non-binary",