Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,7 +121,8 @@ public static Optional<FilingDoc> fromNode(
}
String userDescription = getStringDefault(node, "filing_description", "");
var filingRefNum = unwrap(parser::vetFilingRefNum, node, "reference_number", collector);
Optional<String> filingAttorney = getNonEmptyStringMember(node, "filing_attorney");
Optional<String> filingAttorney =
unwrap(parser::vetFilingAttorney, node, "filing_attorney", collector);
var filingComment = unwrap(parser::vetComment, node, "filing_comment", collector);

String _logName =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -111,16 +115,19 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws
List<Person> otherParties = collectPeople(node, "other_parties", parser, collector);

var varToPartyId = new HashMap<String, PartyId>();
var allPartyIds = new HashSet<PartyId>();
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 =
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -628,6 +641,25 @@ private static String extractNullableString(JsonNode json) {
return null;
}

public static <T> Optional<T> unwrapAtty(
Result<Optional<T>, 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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<FileType> allowed) implements FileNameError {}
public record FileNameTextError(TextVarError err) implements FileNameError {}
Expand Down Expand Up @@ -98,6 +102,13 @@ public Result<Optional<NameAndCode>, CodeError> vetDamageAmount(
public Result<Optional<NameAndCode>, CodeError> vetProcedureRemedy(
Optional<String> maybeProRem, boolean initial, CaseCategory cat);

public Result<Optional<Map<PartyId, List<String>>>, AttorneyError> vetPartyAttorneyMap(
Map<PartyId, List<String>> partyAttyMap,
Collection<PartyId> partyIdSet,
Collection<String> attySet);

public Result<Optional<String>, TextVarError> vetFilingAttorney(Optional<String> filingAttorney);

public Result<Optional<String>, TextVarError> vetEmail(Optional<String> email);

public Result<List<String>, TextVarError> vetPhoneNumbers(List<String> numbers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,51 +527,26 @@ public List<FilingAssociationType> lateStageFilingAssociationAdd(
}
}

DataFieldRow attRow = serializer.allDataFields.getFieldRow("PartyAttorney");
for (Map.Entry<PartyId, List<String>> 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());
continue;
}
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();
selfRepresentedRep.setRef(partyObj);
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,24 +362,11 @@ public JAXBElement<DocumentType> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TylerCodesParser> makeParser(CodeDatabase cd, String courtId) {
public static Optional<CodesParser> makeParser(
CodeDatabase cd, String courtId, boolean isIndividual) {
Optional<CourtLocationInfo> 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.
Expand Down Expand Up @@ -583,6 +587,64 @@ public Result<Optional<NameAndCode>, CodeError> vetDamageAmount(
// Filing Associations
// Anything else not currently checked

// TODO: should rebuild attySet from scratch if it doesn't exist
public Result<Optional<Map<PartyId, List<String>>>, AttorneyError> vetPartyAttorneyMap(
Map<PartyId, List<String>> partyAttyMap,
Collection<PartyId> partyIdSet,
Collection<String> attySet) {
DataFieldRow attRow = allDataFields.getFieldRow("PartyAttorney");
if (!attRow.isvisible) {
return Result.ok(Optional.empty());
}
for (Map.Entry<PartyId, List<String>> 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<Optional<String>, TextVarError> vetFilingAttorney(Optional<String> 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!).
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -107,6 +110,7 @@ public class CourtSchedulingService {
private final ecf4.latest.gov.niem.release.niem.proxy.xsd._4.ObjectFactory proxyObjFac;
private final Map<String, InterviewToFilingInformationConverter> converterMap;
private final CourtRecordMDEService recordFactory;
private final TylerFirmFactory firmFactory;
private final Jurisdiction jurisdiction;

private final Supplier<CodeDatabase> cdSupplier;
Expand Down Expand Up @@ -140,6 +144,11 @@ public CourtSchedulingService(
throw new RuntimeException("Cannot find " + domain + " for court record factory");
}
this.recordFactory = maybeCourt.get();
Optional<TylerFirmFactory> maybeFirmFactory = TylerClients.getEfmFirmFactory(domain);
if (maybeFirmFactory.isEmpty()) {
throw new RuntimeException("Cannot find " + domain + " for firm mde factory");
}
this.firmFactory = maybeFirmFactory.get();
}

@GET
Expand Down Expand Up @@ -252,7 +261,12 @@ public Response getReturnDate(
mediaType = MediaType.valueOf("application/json");
}
InfoCollector collector = new FailFastCollector();
CodesParser parser = new TylerCodesParser(cd, locationInfo.get());
Optional<String> 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<FilingInformation, FilingError> res =
converterMap.get(mediaType.toString()).traverseInterview(allVars, parser, collector);
if (res.isErr()) {
Expand Down Expand Up @@ -569,6 +583,27 @@ public Response reserveCourtDateSync(
return Response.ok(ret).build();
}

private Optional<String> getActiveToken(HttpHeaders httpHeaders) {
String orgHeaderKey =
httpHeaders.getHeaderString(TylerLogin.getHeaderKeyFromJurisdiction(jurisdiction));
String serverKey = httpHeaders.getHeaderString("X-API-KEY");
try (LoginDatabase ld = ldSupplier.get()) {
Optional<AtRest> 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<CourtSchedulingMDE> setupSchedulingPort(HttpHeaders httpHeaders) {
String apiKey = httpHeaders.getHeaderString("X-API-KEY");
Optional<TylerUserNamePassword> creds = Optional.empty();
Expand Down
Loading
Loading