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 3fb9d95cd..a10c0c766 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 @@ -56,6 +56,7 @@ public static Optional fromNode( Map varToPartyId, int sequenceNum, List filingOptions, + boolean isInitialFiling, CodesParser parser, InfoCollector collector) throws FilingError { @@ -115,10 +116,25 @@ public static Optional fromNode( maybeDueDate = Optional.of( LocalDate.ofInstant( - // TODO(brycew): why do we even pass this as a date time? Shouldn't it just be a + // TODO(#47): why do we even pass this as a date time? Shouldn't it just be a // date? Instant.parse(jsonDueDate.asText()), ZoneId.of("America/Chicago"))); } + var dueDateRes = parser.vetDueDate(maybeDueDate, filingType); + Optional dueDate; + if (dueDateRes.isErr()) { + collector.addWrong( + collector + .varBuilder() + .name("due_date") + .datatype("date") + .appendDesc("The due date of the filing, some number of days after the filing.") + .build()); + dueDate = maybeDueDate; + } else { + dueDate = dueDateRes.expect(""); + } + String userDescription = getStringDefault(node, "filing_description", ""); var filingRefNum = unwrap(parser::vetFilingRefNum, node, "reference_number", collector); Optional filingAttorney = @@ -132,23 +148,33 @@ public static Optional fromNode( List preliminaryCopies = getMemberList(node, "preliminary_copies"); List filingParties = getMemberList(node, "filing_parties"); - Optional actionStr = getStringMember(node, "filing_action"); + String actionStr = getStringDefault(node, "filing_action", ""); + Optional maybeAction = + switch (actionStr.toLowerCase()) { + case "e_file" -> Optional.of(FilingAction.E_FILE); + case "efile" -> Optional.of(FilingAction.E_FILE); + case "e_file_and_serve" -> Optional.of(FilingAction.E_FILE_AND_SERVE); + case "efile_and_serve" -> Optional.of(FilingAction.E_FILE_AND_SERVE); + case "serve" -> Optional.of(FilingAction.SERVE); + default -> { + log.warn("Action {} isn't allowed, using default action for the operation", actionStr); + yield Optional.empty(); + } + }; + var actionRes = parser.vetFilingAction(maybeAction, isInitialFiling); + if (actionRes.isErr()) { + collector.addWrong( + collector + .varBuilder() + .name("filing_action") + .appendDesc(actionRes.expectErr("").reason()) + .build()); + } Optional action = Optional.empty(); - if (actionStr.isPresent()) { - String s = actionStr.get(); - if (s.equalsIgnoreCase("e_file") || s.equalsIgnoreCase("efile")) { - action = Optional.of(FilingAction.E_FILE); - } else if (s.equalsIgnoreCase("efile_and_serve") || s.equalsIgnoreCase("e_file_and_serve")) { - action = Optional.of(FilingAction.E_FILE_AND_SERVE); - } else if (s.equalsIgnoreCase("serve")) { - action = Optional.of(FilingAction.SERVE); - } else { - log.warn( - "Filing Action " - + s - + " isn't an allowed action, defaulting to default action for the operation"); - } + if (actionRes.isOk()) { + action = actionRes.expect(""); } + List fullParties = filingParties.stream() .map( @@ -215,7 +241,7 @@ public static Optional fromNode( userDescription, descriptionFromSpec, filingRefNum, - maybeDueDate, + dueDate, fullParties, filingAttorney, goodAttachments.some(), 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 551a23343..97b428f35 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 @@ -318,6 +318,7 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws node.get("comments_to_clerk"), varToPartyId, filingOptions, + isInitialFiling, parser, collector)); if (entities.getFilings().isEmpty()) { @@ -515,6 +516,7 @@ private static List extractFilingDocs( JsonNode clerkComments, Map varToPartyId, List filingOptions, + boolean isInitialFiling, CodesParser parser, InfoCollector collector) throws FilingError { @@ -540,7 +542,13 @@ private static List extractFilingDocs( collector.pushAttributeStack("al_court_bundle.elements[" + i + "]"); Optional maybeDoc = FilingDocDocassembleJacksonDeserializer.fromNode( - elems.get(i), varToPartyId, filingDocs.size(), filingOptions, parser, collector); + elems.get(i), + varToPartyId, + filingDocs.size(), + filingOptions, + isInitialFiling, + parser, + collector); collector.popAttributeStack(); maybeDoc.ifPresent( doc -> { 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 92f124873..0d44e188f 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 @@ -1,5 +1,6 @@ package edu.suffolk.litlab.efsp.server.ecf4; +import com.hubspot.algebra.NullValue; import com.hubspot.algebra.Result; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseCategory; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseType; @@ -8,6 +9,8 @@ import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilingCode; import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilingComponent; import edu.suffolk.litlab.efsp.ecfcodes.tyler.NameAndCode; +import edu.suffolk.litlab.efsp.model.FilingAction; +import edu.suffolk.litlab.efsp.model.FilingDoc; import edu.suffolk.litlab.efsp.model.OptionalService; import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.model.PartyInfo; @@ -15,6 +18,7 @@ import edu.suffolk.litlab.efsp.model.Person.Gender; import edu.suffolk.litlab.efsp.utils.FilingError; import java.math.BigDecimal; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -45,9 +49,17 @@ public sealed interface AttorneyError {} public record NoMultipleAttorneys() implements AttorneyError {} public record RequiredAttorneys() implements AttorneyError {} + public record DueDateRequired() {} + + public record InvalidFilingAction(String reason) {} + public sealed interface FileNameError {} public record FileExtensionNotAllowed(String given, List allowed) implements FileNameError {} public record FileNameTextError(TextVarError err) implements FileNameError {} + + public sealed interface FilingDocError {} + public record DocTooBig(int idx) implements FilingDocError {} + public record CumulativeDocsTooBig(long cumulativeBytes) implements FilingDocError {} // spotless:on // Methods @@ -127,8 +139,16 @@ public Result>>, AttorneyError> vetPartyAttor public Result, TextVarError> vetComment(Optional comment); + public Result, DueDateRequired> vetDueDate( + Optional dueDate, FilingCode filing); + + public Result, InvalidFilingAction> vetFilingAction( + Optional filingAction, boolean isInitialFiling); + public Optional getDocumentDescription( String description, String firstFileName, FilingCode filing); public Result vetFileName(String fileName); + + public Result vetFilingDocSize(List docs); } 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 21847c84c..2bbe4e1be 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 @@ -325,7 +325,6 @@ public JAXBElement filingDocToXml( CaseCategory caseCategory, CaseType motionType, FilingCode filing, - boolean isIndividual, JsonNode miscInfo, InfoCollector collector) throws IOException, FilingError { @@ -342,20 +341,12 @@ public JAXBElement filingDocToXml( docType.setDocumentFileControlID(Ecf4Helper.convertString(refNum)); }); - DataFieldRow dueDateRow = allDataFields.getFieldRow("DueDateAvailableForFilers"); - if (filing.useduedate && dueDateRow.isvisible) { - if (doc.getDueDate().isPresent()) { - DateType cutOffDate = Ecf4Helper.convertDate(doc.getDueDate().get()); - docType.setDocumentInformationCutOffDate(cutOffDate); - } else if (dueDateRow.isrequired) { - InterviewVariable var = - collector.requestVar( - "due_date", - "The due date of the filing, some number of days after the filing.", - "date"); - collector.addRequired(var); - } - } + doc.getDueDate() + .ifPresent( + dueDate -> { + DateType cutOffDate = Ecf4Helper.convertDate(dueDate); + docType.setDocumentInformationCutOffDate(cutOffDate); + }); docType.setDocumentSequenceID(Ecf4Helper.convertString(Integer.toString(doc.sequenceNum()))); 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 a613b6f5b..408ca3d9d 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 @@ -1,6 +1,9 @@ package edu.suffolk.litlab.efsp.server.ecf4.tyler; +import com.hubspot.algebra.NullValue; import com.hubspot.algebra.Result; +import ecf4.latest.gov.niem.niem.niem_core._2.MeasureType; +import ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.courtpolicyresponsemessage_4.DevelopmentPolicyParametersType; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseCategory; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseType; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeDatabase; @@ -15,13 +18,17 @@ import edu.suffolk.litlab.efsp.ecfcodes.tyler.NameAndCode; import edu.suffolk.litlab.efsp.ecfcodes.tyler.OptionalServiceCode; import edu.suffolk.litlab.efsp.ecfcodes.tyler.PartyType; +import edu.suffolk.litlab.efsp.model.FilingAction; +import edu.suffolk.litlab.efsp.model.FilingDoc; import edu.suffolk.litlab.efsp.model.OptionalService; import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.model.PartyInfo; import edu.suffolk.litlab.efsp.model.Person; import edu.suffolk.litlab.efsp.model.Person.Gender; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; +import edu.suffolk.litlab.efsp.server.ecf4.Ecf4Helper; import edu.suffolk.litlab.efsp.utils.FilingError; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -46,25 +53,38 @@ public class TylerCodesParser implements CodesParser { private static Logger log = LoggerFactory.getLogger(TylerCodesParser.class); private final CodeDatabase cd; private final CourtLocationInfo court; + private final DevelopmentPolicyParametersType policy; private final DataFields allDataFields; private final boolean isIndividual; public TylerCodesParser( - CodeDatabase cd, CourtLocationInfo court, DataFields allDataFields, boolean isIndividual) { + CodeDatabase cd, + DevelopmentPolicyParametersType policy, + CourtLocationInfo court, + DataFields allDataFields, + boolean isIndividual) { this.cd = cd; this.court = court; + this.policy = policy; this.allDataFields = allDataFields; this.isIndividual = isIndividual; } - public TylerCodesParser(CodeDatabase cd, CourtLocationInfo court, boolean isIndividual) { - this(cd, court, cd.getDataFields(court.code), isIndividual); + public TylerCodesParser( + CodeDatabase cd, + DevelopmentPolicyParametersType policy, + CourtLocationInfo court, + boolean isIndividual) { + this(cd, policy, court, cd.getDataFields(court.code), isIndividual); } public static Optional makeParser( - CodeDatabase cd, String courtId, boolean isIndividual) { + CodeDatabase cd, + DevelopmentPolicyParametersType policy, + String courtId, + boolean isIndividual) { Optional locationInfo = cd.getFullLocationInfo(courtId); - return locationInfo.map(li -> new TylerCodesParser(cd, li, isIndividual)); + return locationInfo.map(li -> new TylerCodesParser(cd, policy, li, isIndividual)); } /////////////////// Methods that access the codes database. @@ -578,11 +598,6 @@ public Result, CodeError> vetDamageAmount( } //////// Still TODO - // Attorney: Party Attorney and Filing Attorney - // FileName - // DueDate - // Filing Action - // Filing Doc // Filer Types // Filing Associations // Anything else not currently checked @@ -825,6 +840,41 @@ public Result, TextVarError> vetComment(Optional maybeC return Result.ok(Optional.empty()); } + public Result, DueDateRequired> vetDueDate( + Optional dueDate, FilingCode filing) { + DataFieldRow dueDateRow = allDataFields.getFieldRow("DueDateAvailableForFilers"); + if (filing.useduedate && dueDateRow.isvisible) { + if (dueDate.isEmpty() && dueDateRow.isrequired) { + return Result.err(new DueDateRequired()); + } + return Result.ok(dueDate); + } + return Result.ok(Optional.empty()); + } + + public Result, InvalidFilingAction> vetFilingAction( + Optional filingAction, boolean isInitialFiling) { + // From Reference Guide: if no FilingAction is provided, the original default behavior applies: + // * ReviewFiling API w/o service contacts: EFile + // * ReviewFiling API w/ service contacts: EfileAndServe + // * ServeFiling API: Serve + if (filingAction.isPresent()) { + FilingAction act = filingAction.get(); + boolean serviceOnInitial = + switch (this.court.allowserviceoninitial) { + case TRUE -> true; + case FALSE -> false; + case DEFAULT -> allDataFields.getFieldRow("FilingServiceCheckBoxInitial").isvisible; + }; + if (isInitialFiling + && !serviceOnInitial + && (act.equals(FilingAction.E_FILE_AND_SERVE) || act.equals(FilingAction.SERVE))) { + return Result.err(new InvalidFilingAction("Cannot do service on initial filing")); + } + } + return Result.ok(filingAction); + } + public Optional getDocumentDescription( String description, String firstFileName, FilingCode filing) { DataFieldRow row = allDataFields.getFieldRow("DocumentDescription"); @@ -868,4 +918,24 @@ public Result vetFileName(String fileName) { } return Result.ok(fileName); } + + public Result vetFilingDocSize(List docs) { + MeasureType maxIndivDocSize = policy.getMaximumAllowedAttachmentSize(); + long maxEach = Ecf4Helper.sizeMeasureAsBytes(maxIndivDocSize); + long cumulativeBytes = 0; + for (int i = 0; i < docs.size(); i++) { + var doc = docs.get(i); + long docSize = doc.allAttachmentsLength(); + if (docSize > maxEach) { + return Result.err(new DocTooBig(i)); + } + cumulativeBytes += docSize; + } + MeasureType maxTotalDocSize = policy.getMaximumAllowedMessageSize(); + long maxTotal = Ecf4Helper.sizeMeasureAsBytes(maxTotalDocSize); + if (cumulativeBytes > maxTotal) { + return Result.err(new CumulativeDocsTooBig(cumulativeBytes)); + } + return Result.nullOk(); + } } 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 768ce3b6f..a80abf9a1 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 @@ -28,11 +28,13 @@ import ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.casequerymessage_4.CaseQueryMessageType; import ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.caseresponsemessage_4.CaseResponseMessageType; import ecf4.latest.oasis.names.tc.legalxml_courtfiling.wsdl.webservicesprofile_definitions_4_0.CourtRecordMDEPort; +import ecf4.latest.oasis.names.tc.legalxml_courtfiling.wsdl.webservicesprofile_definitions_4_0.FilingReviewMDEPort; import ecf4.latest.tyler.ecf.v5_0.extensions.common.CourtScheduleType; import ecf4.latest.tyler.ecf.v5_0.extensions.reservedateresponse.ReserveDateResponseMessageType; import ecf4.latest.tyler.ecf.v5_0.extensions.returndate.ReturnDateMessageType; import ecf4.latest.tyler.ecf.v5_0.extensions.returndateresponse.ReturnDateResponseMessageType; import ecf4.latest.tyler.efm.wsdl.webservicesprofile_implementation_4_0.CourtRecordMDEService; +import ecf4.latest.tyler.efm.wsdl.webservicesprofile_implementation_4_0.FilingReviewMDEService; import edu.suffolk.litlab.efsp.Jurisdiction; import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; @@ -50,6 +52,7 @@ import edu.suffolk.litlab.efsp.server.ecf4.EcfCaseTypeFactory; import edu.suffolk.litlab.efsp.server.ecf4.EcfCourtSpecificSerializer; import edu.suffolk.litlab.efsp.server.ecf4.Ecfv5CaseTypeFactory; +import edu.suffolk.litlab.efsp.server.ecf4.PolicyCacher; import edu.suffolk.litlab.efsp.server.ecf4.tyler.TylerCodesParser; import edu.suffolk.litlab.efsp.server.utils.Ecfv5XmlHelper; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; @@ -111,7 +114,9 @@ public class CourtSchedulingService { private final Map converterMap; private final CourtRecordMDEService recordFactory; private final TylerFirmFactory firmFactory; + private final FilingReviewMDEService filingFactory; private final Jurisdiction jurisdiction; + private final PolicyCacher policyCacher; private final Supplier cdSupplier; private final Supplier ldSupplier; @@ -120,7 +125,8 @@ public CourtSchedulingService( Map converterMap, TylerDomain domain, Supplier ldSupplier, - Supplier cdSupplier) { + Supplier cdSupplier, + PolicyCacher policyCacher) { this.jurisdiction = domain.jurisdiction(); this.cdSupplier = cdSupplier; this.ldSupplier = ldSupplier; @@ -129,6 +135,11 @@ public CourtSchedulingService( throw new RuntimeException( "Can't find " + domain + " in the SoapClientChooser for CourtScheduler"); } + var maybeReview = SoapClientChooser.getFilingReviewFactory(domain); + if (maybeReview.isEmpty()) { + throw new RuntimeException("Cannot find " + domain + " for filing review factory"); + } + this.filingFactory = maybeReview.get(); this.schedFactory = maybeSchedFactory.get(); this.converterMap = converterMap; this.oasisWrapObjFac = @@ -149,6 +160,11 @@ public CourtSchedulingService( throw new RuntimeException("Cannot find " + domain + " for firm mde factory"); } this.firmFactory = maybeFirmFactory.get(); + if (policyCacher != null) { + this.policyCacher = policyCacher; + } else { + this.policyCacher = new PolicyCacher(); + } } @GET @@ -266,7 +282,18 @@ public Response getReturnDate( 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); + + var filingPort = setupFilingPort(activeToken.get()); + if (filingPort.isEmpty()) { + return Response.status(401).entity("Not logged in to file with " + courtId).build(); + } + var policy = policyCacher.getPolicyFor(filingPort.get(), locationInfo.get().code); + CodesParser parser = + new TylerCodesParser( + cd, + policy.getDevelopmentPolicyParameters().getValue(), + locationInfo.get(), + isIndividual); Result res = converterMap.get(mediaType.toString()).traverseInterview(allVars, parser, collector); if (res.isErr()) { @@ -667,4 +694,24 @@ private static boolean hasError(ResponseMessageType rt) { return !ms.getMessageContentError().isEmpty() || (!errorCodeText.isBlank() && !errorCodeText.equals("0")); } + + private Optional setupFilingPort(String apiToken) { + Optional creds = + TylerUserNamePassword.userCredsFromAuthorization(apiToken); + if (creds.isEmpty()) { + return Optional.empty(); + } + + FilingReviewMDEPort port = makeFilingPort(); + Map ctx = ((BindingProvider) port).getRequestContext(); + List
headersList = List.of(creds.get().toHeader()); + ctx.put(Header.HEADER_LIST, headersList); + return Optional.of(port); + } + + private FilingReviewMDEPort makeFilingPort() { + FilingReviewMDEPort port = filingFactory.getFilingReviewMDEPort(); + ServiceHelpers.setupServicePort((BindingProvider) port); + return port; + } } 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 688fbabaa..7a5f88fb7 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 @@ -123,10 +123,11 @@ public class Ecf4Filer extends EfmCheckableFilingInterface { private final FilingReviewMDEService filingFactory; private final TylerFirmFactory firmFactory; private final ServiceMDEService serviceFactory; - private static final PolicyCacher policyCacher = new PolicyCacher(); + private final PolicyCacher policyCacher; private final Jurisdiction jurisdiction; - public Ecf4Filer(TylerDomain domain, Supplier cdSupplier) { + public Ecf4Filer( + TylerDomain domain, Supplier cdSupplier, PolicyCacher policyCacher) { this.jurisdiction = domain.jurisdiction(); this.cdSupplier = cdSupplier; TylerLogin login = new TylerLogin(domain); @@ -164,6 +165,11 @@ public Ecf4Filer(TylerDomain domain, Supplier cdSupplier) { throw new RuntimeException("Cannot find " + domain + " for firm mde factory"); } this.firmFactory = maybeFirmFactory.get(); + if (policyCacher != null) { + this.policyCacher = policyCacher; + } else { + this.policyCacher = new PolicyCacher(); + } } @Override @@ -172,15 +178,29 @@ public String getHeaderKey() { } public Optional getParser(String courtId, String apiToken) { - boolean isIndividual = getIsIndividual(firmFactory, apiToken); try (CodeDatabase cd = cdSupplier.get()) { - return TylerCodesParser.makeParser(cd, courtId, isIndividual); + return getParser(cd, courtId, apiToken); } catch (SQLException ex) { log.error("Couldn't get CodeDatabase, can't get CodesParser"); return Optional.empty(); } } + private Optional getParser(CodeDatabase cd, String courtId, String apiToken) { + var filingPort = setupFilingPort(apiToken); + if (filingPort.isEmpty()) { + log.error("Couldn't get filingPort, can't get CodesParser"); + return Optional.empty(); + } + boolean isIndividual = getIsIndividual(firmFactory, apiToken); + var policy = + policyCacher + .getPolicyFor(filingPort.get(), courtId) + .getDevelopmentPolicyParameters() + .getValue(); + return TylerCodesParser.makeParser(cd, policy, courtId, isIndividual); + } + private CoreMessageAndNames prepareFiling( FilingInformation info, InfoCollector collector, @@ -220,10 +240,14 @@ private CoreMessageAndNames prepareFiling( collector.error(err); } - boolean isIndividual = getIsIndividual(firmFactory, apiToken); - EcfCourtSpecificSerializer serializer = new EcfCourtSpecificSerializer(cd, locationInfo); - CodesParser parser = new TylerCodesParser(cd, locationInfo, isIndividual); + var maybeParser = getParser(cd, locationInfo.code, apiToken); + if (maybeParser.isEmpty()) { + collector.error( + FilingError.serverError( + "Court setup or apiToken wrong: can't get parser for " + info.getCourtLocation())); + } + var parser = maybeParser.get(); boolean isInitialFiling = info.getPreviousCaseId().isEmpty() && info.getCaseDocketNumber().isEmpty(); boolean isFirstIndexedFiling = info.getPreviousCaseId().isEmpty(); @@ -445,7 +469,6 @@ private CoreMessageAndNames prepareFiling( allCodes.cat(), allCodes.type(), fc, - isIndividual, info.getMiscInfo(), collector); collector.popAttributeStack(); 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 079b4e68e..175f2bd10 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 @@ -10,6 +10,7 @@ import edu.suffolk.litlab.efsp.ecfcodes.CodeUpdater; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeDatabase; import edu.suffolk.litlab.efsp.ecfcodes.tyler.DataFieldRow; +import edu.suffolk.litlab.efsp.server.ecf4.PolicyCacher; import edu.suffolk.litlab.efsp.server.services.AdminUserService; import edu.suffolk.litlab.efsp.server.services.CasesService; import edu.suffolk.litlab.efsp.server.services.CourtSchedulingService; @@ -285,7 +286,8 @@ public JurisdictionServiceHandle getServiceHandle() { return CodeDatabase.fromDS(tylerDomain, this.codeDs); }; - EfmFilingInterface filer = new Ecf4Filer(tylerDomain, cdSupplier); + PolicyCacher policyCacher = new PolicyCacher(); + EfmFilingInterface filer = new Ecf4Filer(tylerDomain, cdSupplier, policyCacher); for (String court : getCourts()) { filingMap.put(court, filer); getCallback().ifPresent(call -> callbackMap.put(court, call)); @@ -325,7 +327,8 @@ public JurisdictionServiceHandle getServiceHandle() { if (tylerDomain.jurisdiction() == Jurisdiction.ILLINOIS) { courtScheduler = Optional.of( - new CourtSchedulingService(converterMap, tylerDomain, ldSupplier, cdSupplier)); + new CourtSchedulingService( + converterMap, tylerDomain, ldSupplier, cdSupplier, policyCacher)); } var filingReview = new FilingReviewService( 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 3280585c3..f1a1d7433 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 @@ -88,7 +88,7 @@ public void setUp() throws IOException { var loc = new CourtLocationInfo("01"); loc.initial = true; loc.subsequent = true; - parser = new TylerCodesParser(cd, loc, true); + parser = new TylerCodesParser(cd, null, 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 d9db7fba3..d67baeeee 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 @@ -11,6 +11,8 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import ecf4.latest.gov.niem.niem.niem_core._2.MeasureType; +import ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.courtpolicyresponsemessage_4.DevelopmentPolicyParametersType; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeDatabase; import edu.suffolk.litlab.efsp.ecfcodes.tyler.CourtLocationInfo; import edu.suffolk.litlab.efsp.ecfcodes.tyler.DataFieldRow; @@ -22,6 +24,7 @@ import edu.suffolk.litlab.efsp.ecfcodes.tyler.OptionalServiceCode; import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.server.ecf4.CodesParser; +import edu.suffolk.litlab.efsp.server.ecf4.Ecf4Helper; import edu.suffolk.litlab.efsp.server.ecf4.tyler.TylerCodesParser; import edu.suffolk.litlab.efsp.utils.FailFastCollector; import edu.suffolk.litlab.efsp.utils.FilingError; @@ -51,7 +54,7 @@ public class FilingDocDocassembleJacksonDeserializerTest { Map varToPartyId; List filingCodes = List.of( - new FilingCode("6553", "idk", "0", "", "", "", false, "", "", "", false, false, "", "")); + new FilingCode("6553", "idk", "0", "", "", "", false, "", "", "", true, false, "", "")); CodesParser parser; @BeforeEach @@ -76,8 +79,19 @@ public void setUp() { List.of( Map.of( "DocumentType", - new DataFieldRow("DocumentType", "Document Type", true, false, "adams")))); - parser = new TylerCodesParser(cd, new CourtLocationInfo("adams"), dataFields, true); + new DataFieldRow("DocumentType", "Document Type", true, false, "adams"), + "DueDateAvailableForFilers", + new DataFieldRow( + "DueDateAvailableForFilers", "Due Date", true, false, "adams")))); + + var maxMeasure = new MeasureType(); + var niem2Fac = new ecf4.latest.gov.niem.niem.niem_core._2.ObjectFactory(); + maxMeasure.setMeasureValue(niem2Fac.createMeasureText(Ecf4Helper.convertText("100"))); + maxMeasure.setMeasureUnitText(Ecf4Helper.convertText("MB")); + var policy = new DevelopmentPolicyParametersType(); + policy.setMaximumAllowedAttachmentSize(maxMeasure); + policy.setMaximumAllowedMessageSize(maxMeasure); + parser = new TylerCodesParser(cd, policy, new CourtLocationInfo("adams"), dataFields, true); } @Test @@ -85,12 +99,13 @@ public void testWeirdJsonShouldBeEmpty() throws JsonMappingException, JsonProcessingException, FilingError { ObjectMapper m = new ObjectMapper(); assertThatThrownBy( - () -> fromNode(m.readTree("null"), varToPartyId, 0, filingCodes, parser, collector)) + () -> + fromNode(m.readTree("null"), varToPartyId, 0, filingCodes, true, parser, collector)) .isInstanceOf(FilingError.class); assertThatThrownBy( - () -> fromNode(m.readTree("[]"), varToPartyId, 0, filingCodes, parser, collector)) + () -> fromNode(m.readTree("[]"), varToPartyId, 0, filingCodes, true, parser, collector)) .isInstanceOf(FilingError.class); - var doc = fromNode(m.readTree("{}"), varToPartyId, 0, filingCodes, parser, collector); + var doc = fromNode(m.readTree("{}"), varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isEmpty(); } @@ -100,7 +115,7 @@ public void noAttachmentsShouldLoadDocument() throws FilingError, IOException { JsonNode node = readFile("old_style_doc.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var attachments = doc.get().getFilingAttachments(); assertThat(attachments.length()).isEqualTo(1); @@ -113,7 +128,7 @@ public void oneAttachmentShouldLoadDocument() throws FilingError, IOException { JsonNode node = readFile("one_attachment.json"); var maybeDoc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(maybeDoc).isPresent(); var doc = maybeDoc.get(); assertThat(doc.getFilingCode()).isEqualTo(filingCodes.get(0)); @@ -135,7 +150,7 @@ public void oneAttachmentAsSecondSequenceShouldLoadDocument() throws FilingError JsonNode node = readFile("one_attachment.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 2, filingCodes, parser, collector); + node, varToPartyId, 2, filingCodes, true, parser, collector); assertThat(doc).isPresent(); assertThat(doc.get().sequenceNum()).isEqualTo(2); var attachments = doc.get().getFilingAttachments(); @@ -152,7 +167,7 @@ public void partyIdNotPresent() throws IOException, FilingError { JsonNode node = readFile("one_attachment.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, Map.of(), 0, filingCodes, parser, collector); + node, Map.of(), 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var parties = doc.get().getFilingPartyIds(); assertThat(parties.size()).isEqualTo(1); @@ -164,7 +179,7 @@ public void oneEnabledOneDisabledShouldLoadOneAttachment() throws FilingError, I JsonNode node = readFile("one_enabled_one_disabled.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var attachments = doc.get().getFilingAttachments(); assertThat(attachments.length()).isEqualTo(1); @@ -177,7 +192,7 @@ public void noEnabledShouldFilingError() throws IOException, FilingError { JsonNode node = readFile("fail_no_enabled_attachment.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).as("Document shouldn't have parsed").isEmpty(); } @@ -186,7 +201,7 @@ public void shouldFallbackToParentDocIfNoEnabled() throws FilingError, IOExcepti JsonNode node = readFile("fallback_to_upper_doc.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var attachments = doc.get().getFilingAttachments(); assertThat(attachments.length()).isEqualTo(1); @@ -200,7 +215,7 @@ public void noDocTypesShouldFail() throws IOException, FilingError { JsonNode node = readFile("fail_missing_doc_types.json"); try { FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); fail("Should have thrown a filing error, didn't"); } catch (FilingError err) { assertThat(err.getType()).isEqualTo(FilingError.Type.WrongValue); @@ -212,7 +227,7 @@ public void twoAttachmentsShouldLoadTwoAttachments() throws FilingError, IOExcep JsonNode node = readFile("two_attachments.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var attachments = doc.get().getFilingAttachments(); assertThat(attachments.length()).isEqualTo(2); @@ -229,7 +244,7 @@ public void hasOptionalServicesShouldParse() throws FilingError, IOException { JsonNode node = readFile("has_optional_services.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var optionalServices = doc.get().getOptionalServices(); assertThat(optionalServices).hasSize(1); @@ -242,7 +257,7 @@ public void filingActionShouldAlwaysParse() throws IOException, FilingError { JsonNode node = readTemplate("template_filing_action.json", emptyData); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); assertThat(doc.get().getFilingAction()).isEmpty(); } @@ -256,7 +271,7 @@ public void filingActionShouldAlwaysParse() throws IOException, FilingError { JsonNode node = readTemplate("template_filing_action.json", data); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, false, parser, collector); assertThat(doc).isPresent(); assertThat(doc.get().getFilingAction()).isPresent(); } @@ -267,7 +282,7 @@ public void testTylerMergeAttachments() throws IOException, FilingError { JsonNode node = readFile("tyler_merge_attachments.json"); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); var attachments = doc.get().getFilingAttachments(); assertThat(attachments.length()).isEqualTo(1); @@ -294,7 +309,7 @@ record InputSaved(String dateInput, String savedDate) {} JsonNode node = readTemplate(INPUT_FILE, testData.dateInput); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); assertThat(doc.get().getDueDate()).isPresent(); assertThat(doc.get().getDueDate().get()).isEqualTo(testData.savedDate); @@ -311,7 +326,7 @@ public void nonTextDueDateShouldBeEmpty() throws IOException, FilingError { JsonNode node = readTemplate(INPUT_FILE, emptyData); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector); + node, varToPartyId, 0, filingCodes, true, parser, collector); assertThat(doc).isPresent(); assertThat(doc.get().getDueDate()).isEmpty(); } @@ -332,7 +347,7 @@ public void invalidDueDateShouldThrow() throws IOException, FilingError { assertThatThrownBy( () -> FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector)) + node, varToPartyId, 0, filingCodes, true, parser, collector)) .withFailMessage("Failed on %s", badData) .isInstanceOf(DateTimeParseException.class); } @@ -356,7 +371,7 @@ public void testMalformedDataUrlShouldThrow() throws IOException { assertThatThrownBy( () -> FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 0, filingCodes, parser, collector)) + node, varToPartyId, 0, filingCodes, true, parser, collector)) .withFailMessage("Using %s", badData) .isInstanceOf(FilingError.class); } 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 cd3a966b9..e6ee0b983 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"), true); + parser = new TylerCodesParser(cd, null, 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 abb32db0e..802af5d21 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,17 +225,17 @@ 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, true); + var parser = new TylerCodesParser(cd, null, loc, true); var doc = FilingDocDocassembleJacksonDeserializer.fromNode( - node, varToPartyId, 2, List.of(filing), parser, collector); + node, varToPartyId, 2, List.of(filing), true, parser, collector); EcfCourtSpecificSerializer cookSer = new EcfCourtSpecificSerializer(cd, loc); ObjectMapper mapper = new ObjectMapper(); JsonNode miscNode = mapper.createObjectNode(); var xmlDoc = cookSer.filingDocToXml( - doc.get(), true, caseCategory, caseType, filing, true, miscNode, collector); + doc.get(), true, caseCategory, caseType, filing, miscNode, collector); assertThat(xmlDoc.getValue().getDocumentSequenceID().getValue()).isEqualTo("2"); assertThat(xmlDoc.getValue().getDocumentDescriptionText().getValue()) .isEqualTo("The Motion to Stay Eviction for Bob Ma"); 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 425993319..dae548b80 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 @@ -90,7 +90,7 @@ public void setUp() { var loc = new CourtLocationInfo("01"); loc.initial = true; - parser = new TylerCodesParser(cd, loc, dataFields, true); + parser = new TylerCodesParser(cd, null, loc, dataFields, true); } @Test @@ -532,7 +532,7 @@ public void noMultipleAttorneys() { var loc = new CourtLocationInfo("01"); loc.initial = true; loc.allowmultipleattorneys = false; - parser = new TylerCodesParser(cd, loc, dataFields, true); + parser = new TylerCodesParser(cd, null, loc, dataFields, true); var map = Map.of(PartyId.Already("1"), List.of("abc", "def")); var partyIds = Set.of(PartyId.Already("1"));