diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java index aaa96e66f..3fb9d95cd 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java @@ -4,7 +4,6 @@ import static edu.suffolk.litlab.efsp.utils.FilingError.serverError; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getIntMember; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getMemberList; -import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getNonEmptyStringMember; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getNumberMember; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getStringDefault; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getStringMember; @@ -122,7 +121,8 @@ public static Optional fromNode( } String userDescription = getStringDefault(node, "filing_description", ""); var filingRefNum = unwrap(parser::vetFilingRefNum, node, "reference_number", collector); - Optional filingAttorney = getNonEmptyStringMember(node, "filing_attorney"); + Optional filingAttorney = + unwrap(parser::vetFilingAttorney, node, "filing_attorney", collector); var filingComment = unwrap(parser::vetComment, node, "filing_comment", collector); String _logName = diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java index 264c15b83..551a23343 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java @@ -17,6 +17,9 @@ import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.model.Person; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; +import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.AttorneyError; +import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.NoMultipleAttorneys; +import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.RequiredAttorneys; import edu.suffolk.litlab.efsp.tyler.TylerEnv; import edu.suffolk.litlab.efsp.utils.FilingError; import edu.suffolk.litlab.efsp.utils.InfoCollector; @@ -28,6 +31,7 @@ import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -111,16 +115,19 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws List otherParties = collectPeople(node, "other_parties", parser, collector); var varToPartyId = new HashMap(); + var allPartyIds = new HashSet(); int perIdx = 0; for (Person user : users) { varToPartyId.put("users[" + perIdx + "]", user.getPartyId()); varToPartyId.put(user.getPartyId().getIdentificationString(), user.getPartyId()); + allPartyIds.add(user.getPartyId()); perIdx++; } perIdx = 0; for (Person party : otherParties) { varToPartyId.put("other_parties[" + perIdx + "]", party.getPartyId()); varToPartyId.put(party.getPartyId().getIdentificationString(), party.getPartyId()); + allPartyIds.add(party.getPartyId()); perIdx++; } users = @@ -209,8 +216,14 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws } entities.setAttorneyIds(extractAttorneyIds(node.get("attorney_ids"))); + var attyMapRes = + parser.vetPartyAttorneyMap( + extractPartyAttorneyMap(node.get("party_to_attorneys"), varToPartyId), + allPartyIds, + entities.getAttorneyIds()); entities.setPartyAttorneyMap( - extractPartyAttorneyMap(node.get("party_to_attorneys"), varToPartyId)); + unwrapAtty(attyMapRes, "party_to_attorneys", collector).orElse(Map.of())); + entities.setServiceContacts( extractServiceContacts(node.get("service_contacts"), varToPartyId, collector)); entities.setLowerCourtInfo(extractLowerCourt(node, collector)); @@ -628,6 +641,25 @@ private static String extractNullableString(JsonNode json) { return null; } + public static Optional unwrapAtty( + Result, AttorneyError> res, String name, InfoCollector collector) + throws FilingError { + if (res.isErr()) { + var attyErr = res.expectErr(""); + var builder = collector.varBuilder().name(name); + switch (attyErr) { + case RequiredAttorneys reqAtty -> + collector.addWrong(builder.appendDesc("Attorneys are required").build()); + case NoMultipleAttorneys noMult -> + collector.addWrong( + builder.appendDesc("Multiple attorneys per party are not allowed").build()); + } + return Optional.empty(); + } else { + return res.expect(""); + } + } + @Override public FilingInformation deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 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 cb4f4570a..92f124873 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 @@ -41,6 +41,10 @@ public record WrongRefVal(String refCode, String refVal, Pattern regex, String m public record NoMatchingRef(String refCode, String refVal) implements CrossReferenceError {} public record MissingRequiredRefs(Set refCodes) implements CrossReferenceError {} + public sealed interface AttorneyError {} + public record NoMultipleAttorneys() implements AttorneyError {} + public record RequiredAttorneys() implements AttorneyError {} + public sealed interface FileNameError {} public record FileExtensionNotAllowed(String given, List allowed) implements FileNameError {} public record FileNameTextError(TextVarError err) implements FileNameError {} @@ -98,6 +102,13 @@ public Result, CodeError> vetDamageAmount( public Result, CodeError> vetProcedureRemedy( Optional maybeProRem, boolean initial, CaseCategory cat); + public Result>>, AttorneyError> vetPartyAttorneyMap( + Map> partyAttyMap, + Collection partyIdSet, + Collection attySet); + + public Result, TextVarError> vetFilingAttorney(Optional filingAttorney); + public Result, TextVarError> vetEmail(Optional email); public Result, TextVarError> vetPhoneNumbers(List numbers); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCaseTypeFactory.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCaseTypeFactory.java index 125654350..d6faf8a8e 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCaseTypeFactory.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/EcfCaseTypeFactory.java @@ -527,18 +527,10 @@ public List lateStageFilingAssociationAdd( } } - DataFieldRow attRow = serializer.allDataFields.getFieldRow("PartyAttorney"); for (Map.Entry> partyAttys : info.getPartyAttorneyMap().entrySet()) { log.info("Setting Attorneys for : {}", partyAttys.getKey()); - Object partyObj; - if (partyIdToRefObj.containsKey(partyAttys.getKey().getIdString())) { - partyObj = partyIdToRefObj.get(partyAttys.getKey().getIdString()); - } else if (partyIdToRefObj.isEmpty()) { - log.warn( - "No current filing parties, but {} is in the current filing?", - partyAttys.getKey().getIdString()); - continue; - } else { + Object partyObj = partyIdToRefObj.get(partyAttys.getKey().getIdString()); + if (partyObj == null) { log.warn( "Can't handle current filing participant ({}) not already added?!", partyAttys.getKey().getIdString()); @@ -546,17 +538,8 @@ public List lateStageFilingAssociationAdd( } ReferenceType repdRef = structObjFac.createReferenceType(); repdRef.setRef(partyObj); - if (!attRow.isvisible) { - continue; - } if (partyAttys.getValue().isEmpty()) { // Is Self-Represented - if (attRow.isrequired) { - InterviewVariable var = - collector.requestVar( - "party_to_attorney", "Attorneys are required for this court", "DADict"); - collector.addRequired(var); - } CaseOfficialType t = ecfCommonObjFac.createCaseOfficialType(); t.getCaseRepresentedPartyReference().add(repdRef); ReferenceType selfRepresentedRep = structObjFac.createReferenceType(); @@ -564,14 +547,6 @@ public List lateStageFilingAssociationAdd( t.setRoleOfPersonReference(selfRepresentedRep); ecfAug.getCaseOtherEntityAttorney().add(t); } else { - if (!courtLocation.allowmultipleattorneys && partyAttys.getValue().size() > 1) { - FilingError err = - FilingError.malformedInterview( - "Court " - + info.getCourtLocation() - + " doesn't allow multiple lawyers per case party."); - collector.error(err); - } for (String attyId : partyAttys.getValue()) { CaseOfficialType t = ecfCommonObjFac.createCaseOfficialType(); t.getCaseRepresentedPartyReference().add(repdRef); 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 aa87c8a9c..21847c84c 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 @@ -362,24 +362,11 @@ public JAXBElement filingDocToXml( DocumentMetadataType metadata = ecfOf.createDocumentMetadataType(); metadata.setRegisterActionDescriptionText(Ecf4Helper.convertText(filing.code)); - DataFieldRow attorneyRow = allDataFields.getFieldRow("FilingFilingAttorneyView"); - if (attorneyRow.isvisible) { - if (doc.getFilingAttorney().isPresent() && !doc.getFilingAttorney().get().isBlank()) { - metadata.setFilingAttorneyID( - Ecf4Helper.convertId(doc.getFilingAttorney().get(), "REFERENCE")); - } else if (!attorneyRow.isrequired || isIndividual) { - // "This field should contain empty values for Individual filers" - metadata.setFilingAttorneyID(Ecf4Helper.convertId("", "")); - } else { - InterviewVariable attVar = - collector.requestVar( - "filing_attorney", "The Attorney that is filing this document", "text"); - collector.addRequired(attVar); - } - } else { - // It's required, even if it's not visible. So keep it empty. - metadata.setFilingAttorneyID(Ecf4Helper.convertId("", "")); - } + metadata.setFilingAttorneyID( + doc.getFilingAttorney() + .map(atty -> Ecf4Helper.convertId(atty, "REFERENCE")) + // It's required, even if it's not visible or an Individual filer. So keep it empty. + .orElse(Ecf4Helper.convertId("", ""))); for (PartyId filingPartyId : doc.getFilingPartyIds()) { metadata 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 69de6306b..a613b6f5b 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 @@ -47,20 +47,24 @@ public class TylerCodesParser implements CodesParser { private final CodeDatabase cd; private final CourtLocationInfo court; private final DataFields allDataFields; + private final boolean isIndividual; - public TylerCodesParser(CodeDatabase cd, CourtLocationInfo court, DataFields allDataFields) { + public TylerCodesParser( + CodeDatabase cd, CourtLocationInfo court, DataFields allDataFields, boolean isIndividual) { this.cd = cd; this.court = court; this.allDataFields = allDataFields; + this.isIndividual = isIndividual; } - public TylerCodesParser(CodeDatabase cd, CourtLocationInfo court) { - this(cd, court, cd.getDataFields(court.code)); + public TylerCodesParser(CodeDatabase cd, CourtLocationInfo court, boolean isIndividual) { + this(cd, court, cd.getDataFields(court.code), isIndividual); } - public Optional makeParser(CodeDatabase cd, String courtId) { + public static Optional makeParser( + CodeDatabase cd, String courtId, boolean isIndividual) { Optional locationInfo = cd.getFullLocationInfo(courtId); - return locationInfo.map(li -> new TylerCodesParser(cd, li)); + return locationInfo.map(li -> new TylerCodesParser(cd, li, isIndividual)); } /////////////////// Methods that access the codes database. @@ -583,6 +587,64 @@ public Result, CodeError> vetDamageAmount( // Filing Associations // Anything else not currently checked + // TODO: should rebuild attySet from scratch if it doesn't exist + public Result>>, AttorneyError> vetPartyAttorneyMap( + Map> partyAttyMap, + Collection partyIdSet, + Collection attySet) { + DataFieldRow attRow = allDataFields.getFieldRow("PartyAttorney"); + if (!attRow.isvisible) { + return Result.ok(Optional.empty()); + } + for (Map.Entry> partyAttys : partyAttyMap.entrySet()) { + log.info("Setting Attorneys for : {}", partyAttys.getKey()); + if (partyIdSet.isEmpty()) { + log.warn( + "No current filing parties, but {} is in the current filing?", + partyAttys.getKey().getIdString()); + continue; + } else if (!partyIdSet.contains(partyAttys.getKey())) { + log.warn( + "Can't handle current filing participant ({}) not already added?!", + partyAttys.getKey().getIdString()); + continue; + } + if (partyAttys.getValue().isEmpty()) { + // Is Self-Represented + if (attRow.isrequired) { + return Result.err(new RequiredAttorneys()); + } + } else { + if (!this.court.allowmultipleattorneys && partyAttys.getValue().size() > 1) { + return Result.err(new NoMultipleAttorneys()); + } + } + for (String atty : partyAttys.getValue()) { + if (!attySet.contains(atty)) { + log.warn("Party attorney {} not present in attorney list? {}", atty, attySet); + } + } + } + + return Result.ok(Optional.of(partyAttyMap)); + } + + // Should only return MissingVar + // TODO: should check that filing attorney is in Atty set + public Result, TextVarError> vetFilingAttorney(Optional filingAttorney) { + DataFieldRow attorneyRow = allDataFields.getFieldRow("FilingFilingAttorneyView"); + if (filingAttorney.isPresent() && filingAttorney.get().isBlank()) { + filingAttorney = Optional.empty(); + } + if (attorneyRow.isvisible) { + if (!filingAttorney.isPresent() && attorneyRow.isrequired && !isIndividual) { + return Result.err(new MissingVar(attorneyRow.regularexpression)); + } + return Result.ok(filingAttorney); + } + return Result.ok(Optional.empty()); + } + /** * Throws if something is wrong; otherwise, an optional email (empty does not mean error!). * diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java index 9d58c1803..768ce3b6f 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java @@ -1,5 +1,6 @@ package edu.suffolk.litlab.efsp.server.services; +import static edu.suffolk.litlab.efsp.server.utils.ServiceHelpers.getIsIndividual; import static edu.suffolk.litlab.efsp.utils.JsonHelpers.isNull; import com.fasterxml.jackson.core.JsonProcessingException; @@ -55,7 +56,9 @@ import edu.suffolk.litlab.efsp.server.utils.MDCWrappers; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; import edu.suffolk.litlab.efsp.tyler.SoapClientChooser; +import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerDomain; +import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.utils.FailFastCollector; import edu.suffolk.litlab.efsp.utils.FilingError; @@ -107,6 +110,7 @@ public class CourtSchedulingService { private final ecf4.latest.gov.niem.release.niem.proxy.xsd._4.ObjectFactory proxyObjFac; private final Map converterMap; private final CourtRecordMDEService recordFactory; + private final TylerFirmFactory firmFactory; private final Jurisdiction jurisdiction; private final Supplier cdSupplier; @@ -140,6 +144,11 @@ public CourtSchedulingService( throw new RuntimeException("Cannot find " + domain + " for court record factory"); } this.recordFactory = maybeCourt.get(); + Optional maybeFirmFactory = TylerClients.getEfmFirmFactory(domain); + if (maybeFirmFactory.isEmpty()) { + throw new RuntimeException("Cannot find " + domain + " for firm mde factory"); + } + this.firmFactory = maybeFirmFactory.get(); } @GET @@ -252,7 +261,12 @@ public Response getReturnDate( mediaType = MediaType.valueOf("application/json"); } InfoCollector collector = new FailFastCollector(); - CodesParser parser = new TylerCodesParser(cd, locationInfo.get()); + Optional activeToken = getActiveToken(httpHeaders); + if (activeToken.isEmpty()) { + return Response.status(401).entity("Not logged in to file with " + courtId).build(); + } + boolean isIndividual = getIsIndividual(firmFactory, activeToken.get()); + CodesParser parser = new TylerCodesParser(cd, locationInfo.get(), isIndividual); Result res = converterMap.get(mediaType.toString()).traverseInterview(allVars, parser, collector); if (res.isErr()) { @@ -569,6 +583,27 @@ public Response reserveCourtDateSync( return Response.ok(ret).build(); } + private Optional getActiveToken(HttpHeaders httpHeaders) { + String orgHeaderKey = + httpHeaders.getHeaderString(TylerLogin.getHeaderKeyFromJurisdiction(jurisdiction)); + String serverKey = httpHeaders.getHeaderString("X-API-KEY"); + try (LoginDatabase ld = ldSupplier.get()) { + Optional atRest = ld.getAtRestInfo(serverKey); + if (atRest.isEmpty()) { + return Optional.empty(); + } + String orgToken = httpHeaders.getHeaderString(orgHeaderKey); + if (orgToken == null || orgToken.isBlank()) { + return Optional.empty(); + } + MDC.put(MDCWrappers.USER_ID, Hasher.makeHash(orgToken)); + return Optional.of(orgToken); + } catch (SQLException ex) { + log.error("SQL Error", ex); + return Optional.empty(); + } + } + private Optional setupSchedulingPort(HttpHeaders httpHeaders) { String apiKey = httpHeaders.getHeaderString("X-API-KEY"); Optional creds = Optional.empty(); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java index 9b64c78dd..5dd15cc70 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java @@ -11,13 +11,10 @@ import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.UserDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; -import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeDatabase; -import edu.suffolk.litlab.efsp.ecfcodes.tyler.CourtLocationInfo; import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.FilingResult; import edu.suffolk.litlab.efsp.model.Person; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; -import edu.suffolk.litlab.efsp.server.ecf4.tyler.TylerCodesParser; import edu.suffolk.litlab.efsp.server.services.impl.EfmFilingInterface; import edu.suffolk.litlab.efsp.server.setup.EfmRestCallbackInterface; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; @@ -70,14 +67,12 @@ public class FilingReviewService { private final Map callbackInterfaces; private final OrgMessageSender msgSender; - private final Supplier cdSupplier; private final Supplier ldSupplier; private final Supplier udSupplier; private final EndpointReflection ef; public FilingReviewService( Jurisdiction jurisdiction, - Supplier cdSupplier, Supplier ldSupplier, Supplier udSupplier, Map converterMap, @@ -87,7 +82,6 @@ public FilingReviewService( this.converterMap = converterMap; this.filingInterfaces = filingInterfaces; this.callbackInterfaces = callbackInterfaces; - this.cdSupplier = cdSupplier; this.ldSupplier = ldSupplier; this.udSupplier = udSupplier; this.msgSender = msgSender; @@ -194,15 +188,15 @@ public Response checkFilingForReview( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - Result maybeParser = makeParser(courtId); - if (maybeParser.isErr()) { - return maybeParser.unwrapErrOrElseThrow(); - } - var parser = maybeParser.unwrapOrElseThrow(); Optional activeToken = getActiveToken(httpHeaders, filer.getHeaderKey()); if (activeToken.isEmpty()) { return Response.status(401).entity("Not logged in to file with " + courtId).build(); } + Result maybeParser = makeParser(courtId, activeToken.get(), filer); + if (maybeParser.isErr()) { + return maybeParser.unwrapErrOrElseThrow(); + } + var parser = maybeParser.unwrapOrElseThrow(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } @@ -237,15 +231,15 @@ public Response calculateFilingFees( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - Result maybeParser = makeParser(courtId); - if (maybeParser.isErr()) { - return maybeParser.unwrapErrOrElseThrow(); - } - var parser = maybeParser.unwrapOrElseThrow(); Optional activeToken = getActiveToken(httpHeaders, filer.getHeaderKey()); if (activeToken.isEmpty()) { return Response.status(401).entity("Not logged in to file with " + courtId).build(); } + Result maybeParser = makeParser(courtId, activeToken.get(), filer); + if (maybeParser.isErr()) { + return maybeParser.unwrapErrOrElseThrow(); + } + var parser = maybeParser.unwrapOrElseThrow(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } @@ -278,15 +272,15 @@ public Response getServiceTypes( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - Result maybeParser = makeParser(courtId); - if (maybeParser.isErr()) { - return maybeParser.unwrapErrOrElseThrow(); - } - var parser = maybeParser.unwrapOrElseThrow(); Optional activeToken = getActiveToken(httpHeaders, filer.getHeaderKey()); if (activeToken.isEmpty()) { return Response.status(401).entity("Not logged in to file with " + courtId).build(); } + Result maybeParser = makeParser(courtId, activeToken.get(), filer); + if (maybeParser.isErr()) { + return maybeParser.unwrapErrOrElseThrow(); + } + var parser = maybeParser.unwrapOrElseThrow(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } @@ -381,7 +375,7 @@ private Response fileOrServe( log.error("SQL Error: ", ex); } Result maybeInfo = - parseFiling(httpHeaders, allVars, filer, courtId, mediaType); + parseFiling(httpHeaders, allVars, filer, courtId, activeToken.get(), mediaType); if (maybeInfo.isErr()) { return maybeInfo.unwrapErrOrElseThrow(); } @@ -475,13 +469,14 @@ private Result parseFiling( String allVars, EfmFilingInterface filer, String courtId, + String activeToken, MediaType mediaType) { log.trace("Court id: {}", courtId); if (!converterMap.containsKey(mediaType.toString())) { return Result.err( Response.status(415).entity("We only support " + converterMap.keySet()).build()); } - Result maybeParser = makeParser(courtId); + Result maybeParser = makeParser(courtId, activeToken, filer); if (maybeParser.isErr()) { return maybeParser.propagateErr(); } @@ -586,12 +581,12 @@ private Result checkFilingInterfaces(String courtI return Result.ok(filingInterfaces.get(courtId)); } - private Result makeParser(String courtId) { - CodeDatabase cd = cdSupplier.get(); - Optional info = cd.getFullLocationInfo(courtId); - if (info.isEmpty()) { + private Result makeParser( + String courtId, String apiToken, EfmFilingInterface filer) { + var parser = filer.getParser(courtId, apiToken); + if (parser.isEmpty()) { return Result.err(Response.status(404).entity("Cannot send filing to " + courtId).build()); } - return Result.ok(new TylerCodesParser(cd, info.get())); + return Result.ok(parser.get()); } } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmFilingInterface.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmFilingInterface.java index 2150df51a..820092e6c 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmFilingInterface.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmFilingInterface.java @@ -4,10 +4,12 @@ import com.hubspot.algebra.Result; import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.FilingResult; +import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; import edu.suffolk.litlab.efsp.utils.FilingError; import edu.suffolk.litlab.efsp.utils.InfoCollector; import jakarta.ws.rs.core.Response; import java.time.LocalDate; +import java.util.Optional; public interface EfmFilingInterface { public enum ApiChoice { @@ -54,6 +56,8 @@ public Response getFilingService( public Response cancelFiling(String courtId, String filingId, String apiToken); + public Optional getParser(String courtId, String apiToken); + /** * TYLER ONLY at the moment: returns a list of disclaimers that must be shown to the user before * filing. diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/Ecf4Filer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/Ecf4Filer.java index c8460677d..688fbabaa 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/Ecf4Filer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/Ecf4Filer.java @@ -1,6 +1,6 @@ package edu.suffolk.litlab.efsp.server.setup.tyler; -import static edu.suffolk.litlab.efsp.server.utils.ServiceHelpers.setupFirmPort; +import static edu.suffolk.litlab.efsp.server.utils.ServiceHelpers.getIsIndividual; import static edu.suffolk.litlab.efsp.stdlib.StdLib.exists; import com.hubspot.algebra.Result; @@ -74,8 +74,6 @@ import edu.suffolk.litlab.efsp.tyler.SoapClientChooser; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerDomain; -import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; -import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.utils.FailFastCollector; @@ -173,6 +171,16 @@ public String getHeaderKey() { return this.headerKey; } + public Optional getParser(String courtId, String apiToken) { + boolean isIndividual = getIsIndividual(firmFactory, apiToken); + try (CodeDatabase cd = cdSupplier.get()) { + return TylerCodesParser.makeParser(cd, courtId, isIndividual); + } catch (SQLException ex) { + log.error("Couldn't get CodeDatabase, can't get CodesParser"); + return Optional.empty(); + } + } + private CoreMessageAndNames prepareFiling( FilingInformation info, InfoCollector collector, @@ -212,8 +220,10 @@ private CoreMessageAndNames prepareFiling( collector.error(err); } + boolean isIndividual = getIsIndividual(firmFactory, apiToken); + EcfCourtSpecificSerializer serializer = new EcfCourtSpecificSerializer(cd, locationInfo); - CodesParser parser = new TylerCodesParser(cd, locationInfo); + CodesParser parser = new TylerCodesParser(cd, locationInfo, isIndividual); boolean isInitialFiling = info.getPreviousCaseId().isEmpty() && info.getCaseDocketNumber().isEmpty(); boolean isFirstIndexedFiling = info.getPreviousCaseId().isEmpty(); @@ -405,26 +415,6 @@ private CoreMessageAndNames prepareFiling( long maxSize = Ecf4Helper.sizeMeasureAsBytes(maxIndivDocSize); long cumulativeBytes = 0; - Optional firmClient = setupFirmPort(firmFactory, apiToken); - boolean isIndividual = - firmClient - .map( - port -> { - try { - var resp = port.getFirm(); - if (TylerErrorCodes.hasError(resp)) { - log.warn( - "GetFirm returned an error: {}, {}", - resp.getError().getErrorCode(), - resp.getError().getErrorText()); - } - return resp.getFirm().isIsIndividual(); - } catch (Exception ex) { - log.warn("Exception when getting firm info for individual?:", ex); - return true; - } - }) - .orElse(true); Map filingIdToObj = new HashMap<>(); int seqNum = 0; for (FilingDoc filingDoc : info.getFilings()) { diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/TylerModuleSetup.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/TylerModuleSetup.java index 206513800..079b4e68e 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/TylerModuleSetup.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/setup/tyler/TylerModuleSetup.java @@ -330,7 +330,6 @@ public JurisdictionServiceHandle getServiceHandle() { var filingReview = new FilingReviewService( getJurisdiction(), - cdSupplier, ldSupplier, udSupplier, converterMap, diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java index 360c1ae4a..68ee8ebba 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java @@ -7,6 +7,7 @@ import edu.suffolk.litlab.efsp.db.model.AtRest; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeDatabase; import edu.suffolk.litlab.efsp.server.auth.TylerLogin; +import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; @@ -123,6 +124,30 @@ public static Response.ResponseBuilder getCourts( } } + public static boolean getIsIndividual(TylerFirmFactory firmFactory, String tylerToken) { + Optional firmClient = setupFirmPort(firmFactory, tylerToken); + boolean isIndividual = + firmClient + .map( + port -> { + try { + var resp = port.getFirm(); + if (TylerErrorCodes.hasError(resp)) { + log.warn( + "GetFirm returned an error: {}, {}", + resp.getError().getErrorCode(), + resp.getError().getErrorText()); + } + return resp.getFirm().isIsIndividual(); + } catch (Exception ex) { + log.warn("Exception when getting firm info for individual?:", ex); + return true; + } + }) + .orElse(true); + return isIndividual; + } + public static Optional setupFirmPort( TylerFirmFactory firmFactory, HttpHeaders httpHeaders, 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 708e07064..3280585c3 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 @@ -82,11 +82,13 @@ public void setUp() throws IOException { "PartyLastName", new DataFieldRow("PartyLastName", "last name", true, false, "adams"), "PartyPhone", - new DataFieldRow("PartyPhone", "", true, false, "adams"))))); + new DataFieldRow("PartyPhone", "", true, false, "adams"), + "PartyAttorney", + new DataFieldRow("PartyAttorney", "Attorney", true, false, "adams"))))); var loc = new CourtLocationInfo("01"); loc.initial = true; loc.subsequent = true; - parser = new TylerCodesParser(cd, loc); + parser = new TylerCodesParser(cd, loc, true); } private String getFileContents(String inFileName) throws IOException { diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializerTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializerTest.java index 47095a24f..d9db7fba3 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializerTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializerTest.java @@ -77,7 +77,7 @@ public void setUp() { Map.of( "DocumentType", new DataFieldRow("DocumentType", "Document Type", true, false, "adams")))); - parser = new TylerCodesParser(cd, new CourtLocationInfo("adams"), dataFields); + parser = new TylerCodesParser(cd, new CourtLocationInfo("adams"), dataFields, true); } @Test 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 086495fd4..cd3a966b9 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 @@ -50,7 +50,7 @@ public void setUp() { .thenReturn(new DataFieldRow("PartyNameSuffix", "", false, false, "adams")); when(allDataFields.getFieldRow("PartyGender")) .thenReturn(new DataFieldRow("PartyGender", "", true, false, "adams")); - parser = new TylerCodesParser(cd, new CourtLocationInfo("adams")); + parser = new TylerCodesParser(cd, new CourtLocationInfo("adams"), true); } @Test 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 6c6677f2f..abb32db0e 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 @@ -225,7 +225,7 @@ public void shouldParseDoc() throws IOException, FilingError { var varToPartyId = Map.of("users[0]", PartyId.CurrentFilingNew("abc")); JsonNode node = readFile("one_attachment.json"); - var parser = new TylerCodesParser(cd, loc); + var parser = new TylerCodesParser(cd, loc, true); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( node, varToPartyId, 2, List.of(filing), parser, collector); 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 530659a0c..425993319 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 @@ -18,10 +18,12 @@ import edu.suffolk.litlab.efsp.ecfcodes.tyler.NameAndCode; import edu.suffolk.litlab.efsp.ecfcodes.tyler.OptionalServiceCode; import edu.suffolk.litlab.efsp.model.OptionalService; +import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.BadCode; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.NoMatchingCode; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.NoMatchingRef; +import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.NoMultipleAttorneys; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.RequiredCodeNotPresent; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.TooLongVar; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.WrongRefVal; @@ -31,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -83,11 +86,11 @@ public void setUp() { .thenReturn(List.of(new NameAndCode("SubType code", "998877"))); when(cd.getFilingType("01", exampleCategory.code, exampleCaseType.code, true)) .thenReturn(List.of(filingCode)); + dataFields = mock(DataFields.class); + var loc = new CourtLocationInfo("01"); loc.initial = true; - - dataFields = mock(DataFields.class); - parser = new TylerCodesParser(cd, loc, dataFields); + parser = new TylerCodesParser(cd, loc, dataFields, true); } @Test @@ -514,48 +517,75 @@ public void testProcedureRemedyType() { var res = parser.vetProcedureRemedy(Optional.of("2244"), true, exampleCatProcRem); assertThat(res).containsOk(Optional.of(procRem)); } + } + + @Nested + class AttorneyTests { + @BeforeEach + public void setup() { + when(dataFields.getFieldRow("PartyAttorney")) + .thenReturn(new DataFieldRow("PartyAttorney", "party attorney", true, false, "01")); + } + + @Test + public void noMultipleAttorneys() { + var loc = new CourtLocationInfo("01"); + loc.initial = true; + loc.allowmultipleattorneys = false; + parser = new TylerCodesParser(cd, loc, dataFields, true); + + var map = Map.of(PartyId.Already("1"), List.of("abc", "def")); + var partyIds = Set.of(PartyId.Already("1")); + var attySet = Set.of("abc", "def"); + var res = parser.vetPartyAttorneyMap(map, partyIds, attySet); + assertThat(res).isErr().extractingErr().isInstanceOf(NoMultipleAttorneys.class); + + loc.allowmultipleattorneys = true; + var res2 = parser.vetPartyAttorneyMap(map, partyIds, attySet); + assertThat(res2).isOk(); + } + } + + @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); + } - @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")); - } + @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")); } } }