Skip to content

Commit 1aaf247

Browse files
Vermont capable (#28)
* Add Env var for S3 Endpoint Allows devs to use localstack as an S3 mock. https://github.com/localstack/localstack * Bake jurisdiction into the URL Instead of trying to keep it as a separate dropdown in the profile? That no one will use? * Add vermont * Change tests to use jurisdictions Deleted two duplicate tests at the top of `tests.py` that are also present in `test_smoke.py`.
1 parent ec22230 commit 1aaf247

35 files changed

Lines changed: 275 additions & 298 deletions

‎efile_app/efile/api/auth_views.py‎

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,31 @@ class AuthAPIViews(APIResponseMixin):
2121
"""API views for authentication"""
2222

2323
@staticmethod
24-
def get_state_from_request(request):
25-
"""Extract state from request URL or parameters"""
26-
# Try to get state from query parameters first
27-
state = request.GET.get("state")
28-
if state:
29-
return state.lower()
30-
31-
# Try to extract from URL path (e.g., /jurisdictions/illinois/)
32-
path = request.path
33-
if "/jurisdictions/" in path:
34-
path_parts = path.split("/jurisdictions/")
35-
if len(path_parts) > 1:
36-
state_part = path_parts[1].split("/")[0]
37-
if state_part:
38-
return state_part.lower()
39-
40-
# Default to Illinois if no state found
41-
return "illinois"
24+
def get_jurisdiction_from_request(request):
25+
"""Extract jurisdiction from request URL or parameters"""
26+
# Try to get jurisdiction from query parameters first
27+
jurisdiction = request.GET.get("jurisdiction")
28+
if jurisdiction:
29+
return jurisdiction.lower()
30+
return None
4231

4332
@staticmethod
44-
def get_tyler_token(request, state=None):
33+
def get_tyler_token(request, jurisdiction=None):
4534
"""Helper method to retrieve Tyler token from various sources"""
46-
if state is None:
47-
state = AuthAPIViews.get_state_from_request(request)
35+
if jurisdiction is None:
36+
jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request)
4837

4938
auth_tokens = request.session.get("auth_tokens", {})
5039
logger.debug(f"Auth tokens in session: {auth_tokens}")
5140

5241
# Try different Tyler token key formats
5342
tyler_token = (
54-
auth_tokens.get(f"TYLER-TOKEN-{state.upper()}")
55-
or auth_tokens.get(f"tyler_token_{state}")
56-
or auth_tokens.get(f"tyler-token-{state}")
43+
auth_tokens.get(f"TYLER-TOKEN-{jurisdiction.upper()}")
44+
or auth_tokens.get(f"tyler_token_{jurisdiction}")
45+
or auth_tokens.get(f"tyler-token-{jurisdiction}")
5746
)
5847

59-
if tyler_token:
60-
return tyler_token
61-
62-
return None
48+
return tyler_token
6349

6450
@staticmethod
6551
@require_http_methods(["POST"])
@@ -107,25 +93,25 @@ def user_profile(request):
10793
"""Get current user profile from external Suffolk eFile API"""
10894
try:
10995
try:
110-
# Get state and Tyler token dynamically
111-
state = AuthAPIViews.get_state_from_request(request)
112-
tyler_token = AuthAPIViews.get_tyler_token(request, state)
96+
# Get jurisdiction and Tyler token dynamically
97+
jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request)
98+
tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction)
11399
api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None)
114100

115101
headers = {
116102
"Content-Type": "application/json",
117-
"User-Agent": f"{state.title()}-eFile-Client/1.0",
103+
"User-Agent": f"{jurisdiction.title()}-eFile-Client/1.0",
118104
"X-API-Key": api_key if api_key else "",
119105
}
120106

121107
# Add Tyler token if available
122108
if tyler_token:
123-
headers[f"tyler-token-{state}"] = tyler_token
109+
headers[f"tyler-token-{jurisdiction}"] = tyler_token
124110
else:
125111
# Log that no token was found for debugging
126-
logger.info("No Tyler token found for state '%s' in Suffolk eFile API request", state)
112+
logger.info("No Tyler token found for state '%s' in Suffolk eFile API request", jurisdiction)
127113

128-
url = f"{settings.EFSP_URL}/jurisdictions/{state}/firmattorneyservice/firm"
114+
url = f"{settings.EFSP_URL}/jurisdictions/{jurisdiction}/firmattorneyservice/firm"
129115
logger.debug("GET %s header keys=%s", url, list(headers.keys()))
130116
api_response = requests.get(url, headers=headers, timeout=10)
131117
logger.debug(
@@ -293,11 +279,11 @@ def external_auth(request):
293279
return AuthAPIViews.error_response("Username and password required")
294280

295281
# Authenticate with Suffolk eFile API
296-
state = AuthAPIViews.get_state_from_request(request)
282+
jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request)
297283
auth_response = requests.post(
298-
f"{settings.EFSP_URL}/jurisdictions/{state}/auth/login",
284+
f"{settings.EFSP_URL}/jurisdictions/{jurisdiction}/auth/login",
299285
json={"username": username, "password": password},
300-
headers={"Content-Type": "application/json", "User-Agent": f"{state.title()}-eFile-Client/1.0"},
286+
headers={"Content-Type": "application/json", "User-Agent": f"{jurisdiction.title()}-eFile-Client/1.0"},
301287
)
302288

303289
if auth_response.status_code == 200:
@@ -307,17 +293,17 @@ def external_auth(request):
307293
request.session["auth_tokens"] = {
308294
"access_token": auth_data.get("access_token"),
309295
"refresh_token": auth_data.get("refresh_token"),
310-
f"tyler_token_{state}": auth_data.get(f"tyler_token_{state}"),
296+
f"tyler_token_{jurisdiction}": auth_data.get(f"tyler_token_{jurisdiction}"),
311297
"expires_in": auth_data.get("expires_in"),
312-
"state": state, # Store the state for future reference
298+
"state": jurisdiction, # Store the state for future reference
313299
}
314300

315301
return AuthAPIViews.success_response(
316302
{
317303
"authenticated": True,
318304
"user": auth_data.get("user", {}),
319-
"state": state,
320-
"has_tyler_token": f"tyler_token_{state}" in auth_data,
305+
"state": jurisdiction,
306+
"has_tyler_token": f"tyler_token_{jurisdiction}" in auth_data,
321307
},
322308
"External authentication successful",
323309
)
@@ -364,12 +350,14 @@ def external_profile(request):
364350
def tyler_token(request):
365351
"""Get Tyler token and API key for external form submissions"""
366352
try:
367-
# Get state and Tyler token dynamically
368-
state = AuthAPIViews.get_state_from_request(request)
369-
tyler_token = AuthAPIViews.get_tyler_token(request, state)
353+
# Get jurisdiction and Tyler token dynamically
354+
jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request)
355+
tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction)
370356
api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None)
371357

372-
return AuthAPIViews.success_response({"tyler_token": tyler_token, "api_key": api_key, "state": state})
358+
return AuthAPIViews.success_response(
359+
{"tyler_token": tyler_token, "api_key": api_key, "state": jurisdiction}
360+
)
373361
except Exception as e:
374362
return AuthAPIViews.error_response(f"Error: {str(e)}")
375363

@@ -378,28 +366,28 @@ def tyler_token(request):
378366
def payment_accounts(request):
379367
"""Get payment accounts from Suffolk eFile API with proper authentication"""
380368
try:
381-
# Get state and Tyler token dynamically
382-
state = AuthAPIViews.get_state_from_request(request)
383-
tyler_token = AuthAPIViews.get_tyler_token(request, state)
369+
# Get jurisdiction and Tyler token dynamically
370+
jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request)
371+
tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction)
384372
api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None)
385373

386374
headers = {
387375
"Content-Type": "application/json",
388-
"User-Agent": f"{state.title()}-eFile-Client/1.0",
376+
"User-Agent": f"{jurisdiction.title()}-eFile-Client/1.0",
389377
"X-API-Key": api_key if api_key else "",
390378
}
391379

392380
# Add Tyler token if available
393381
if tyler_token:
394-
headers[f"tyler-token-{state}"] = tyler_token
382+
headers[f"tyler-token-{jurisdiction}"] = tyler_token
395383
else:
396384
# Log that no token was found for debugging
397385
logger.info(
398386
"No Tyler token found for state '%s' in Suffolk eFile payment accounts request",
399-
state,
387+
jurisdiction,
400388
)
401389

402-
url = f"{settings.EFSP_URL}/jurisdictions/{state}/payments/payment-accounts/"
390+
url = f"{settings.EFSP_URL}/jurisdictions/{jurisdiction}/payments/payment-accounts/"
403391
logger.debug("GET %s header keys=%s", url, list(headers.keys()))
404392
api_response = requests.get(url, headers=headers, timeout=10)
405393
logger.debug(

‎efile_app/efile/api/dropdown_views.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def get_filing_types(request):
137137
court_code = request.GET.get("court")
138138
case_type_id = request.GET.get("case_type") or request.GET.get("parent") # Support both flows
139139
case_category_id = request.GET.get("case_category")
140-
jurisdiction = request.GET.get("jurisdiction", "illinois")
140+
jurisdiction = request.GET.get("jurisdiction")
141141
existing_case = request.GET.get("existing_case")
142142

143143
# Set initial flag based on existing_case parameter:
@@ -205,12 +205,12 @@ def get_courts(request):
205205
try:
206206
auth_tokens = get_auth_tokens(request)
207207

208-
jurisdiction = request.GET.get("jurisdiction", "illinois")
208+
jurisdiction = request.GET.get("jurisdiction", "")
209209
user_zip = request.GET.get("user_zip")
210210
user_county = request.GET.get("user_county")
211211

212212
# Make API call to external jurisdiction endpoint
213-
api_url = f"https://efile.suffolklitlab.org/jurisdictions/{jurisdiction}/codes/courts/?with_names=True"
213+
api_url = f"{settings.EFSP_URL}/jurisdictions/{jurisdiction}/codes/courts/?with_names=True"
214214

215215
try:
216216
# Make the API request with auth tokens if available

‎efile_app/efile/context_processors.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def jurisdiction_context(request):
1515
}
1616

1717
return {
18-
"current_jurisdiction": current_jurisdiction,
18+
"jurisdiction": current_jurisdiction,
1919
# Returns just a subset of the keys in the config
2020
"jurisdiction_config": config,
2121
"available_jurisdictions": short_configs,

‎efile_app/efile/settings_base.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY", "")
9999
AWS_S3_BUCKET_NAME = os.getenv("AWS_S3_BUCKET_NAME", "")
100100
AWS_S3_REGION_NAME = os.getenv("AWS_S3_REGION_NAME", "us-east-1")
101+
AWS_S3_ENDPOINT_URL = os.getenv("AWS_S3_ENDPOINT_URL", None)
101102

102103
# File Upload Settings
103104
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
jurisdiction:
3+
name: "Vermont eFile"
4+
code: "vermont"
5+
display_name: "Vermont"
6+
icon: "fas fa-balance-scale"
7+
api_endpoint: "/api/vermont"
8+
9+
# State metadata, for the address
10+
state:
11+
code: "VT"
12+
name: "Vermont"
13+
jurisdiction: "vermont"
14+
15+
# Inherit from base case types and customize
16+
inherits_from: "base-case-types.yaml"
17+
18+
case_types:
19+
name_change:
20+
extends: "base_case_types.name_change"

‎efile_app/efile/static/js/api-utils.js‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,32 @@ class ApiUtils {
1313
this.clearExpiredCache();
1414
}
1515

16+
/**
17+
* Get current jurisdiction from session or default to Illinois
18+
* @returns {string} Current jurisdiction code
19+
*/
20+
getCurrentJurisdiction() {
21+
// TODO(brycew): should we make a server query? Probably not necessary?
22+
//const response = await fetch('/api/jurisdiction/current/');
23+
//const result = await response.json();
24+
25+
// Try to get from jurisdiction selector first
26+
const jurisdictionSelect = document.getElementById("jurisdictionSelect");
27+
if (jurisdictionSelect && jurisdictionSelect.value) {
28+
return jurisdictionSelect.value;
29+
}
30+
31+
// Try to get from profile modal selector
32+
const profileSelect = document.getElementById("profileJurisdictionSelect");
33+
if (profileSelect && profileSelect.value) {
34+
return profileSelect.value;
35+
}
36+
37+
// Default to Illinois if nothing found
38+
console.log("Returning illinois for current, likely shouldn't")
39+
return "illinois";
40+
}
41+
1642
getCache() {
1743
try {
1844
const cached = localStorage.getItem('apiResponseCache');
@@ -166,7 +192,7 @@ class ApiUtils {
166192
case '500':
167193
return new Error('Server error. Please try again later.');
168194
default:
169-
return new Error('An unexpected error occurred.');
195+
return new Error(`An unexpected error occurred (${status}).`);
170196
}
171197
}
172198
return error;

‎efile_app/efile/static/js/cascading-dropdowns.js‎

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class CascadingDropdowns {
4848

4949
async loadCourtsWithUserContext() {
5050
const params = {};
51-
const currentJurisdiction = this.getCurrentJurisdiction();
51+
const currentJurisdiction = apiUtils.getCurrentJurisdiction();
5252

5353
// Set the jurisdiction parameter
5454
params.jurisdiction = currentJurisdiction;
@@ -83,7 +83,6 @@ class CascadingDropdowns {
8383
} else {
8484
console.warn("No user profile available for courts loading");
8585
}
86-
8786
await this.loadDropdownData("court", "/api/dropdowns/courts/", params);
8887
}
8988

@@ -97,7 +96,7 @@ class CascadingDropdowns {
9796
'<i class="fas fa-spinner fa-spin"></i> Loading your information...';
9897
}
9998

100-
const response = await this.makeRequest("/api/auth/profile/");
99+
const response = await this.makeRequest("/api/auth/profile/", {"jurisdiction": apiUtils.getCurrentJurisdiction()});
101100

102101
if (response.success) {
103102
if (statusElement) {
@@ -207,7 +206,7 @@ class CascadingDropdowns {
207206
let params = {};
208207

209208
// Always add jurisdiction
210-
params.jurisdiction = this.getCurrentJurisdiction();
209+
params.jurisdiction = apiUtils.getCurrentJurisdiction();
211210

212211
// Add additional context parameters based on the field
213212
if (fieldId === "court") {
@@ -990,27 +989,6 @@ class CascadingDropdowns {
990989
dynamicSections.innerHTML = '';
991990
}
992991
}
993-
994-
/**
995-
* Get current jurisdiction from session or default to Illinois
996-
* @returns {string} Current jurisdiction code
997-
*/
998-
getCurrentJurisdiction() {
999-
// Try to get from jurisdiction selector first
1000-
const jurisdictionSelect = document.getElementById("jurisdictionSelect");
1001-
if (jurisdictionSelect && jurisdictionSelect.value) {
1002-
return jurisdictionSelect.value;
1003-
}
1004-
1005-
// Try to get from profile modal selector
1006-
const profileSelect = document.getElementById("profileJurisdictionSelect");
1007-
if (profileSelect && profileSelect.value) {
1008-
return profileSelect.value;
1009-
}
1010-
1011-
// Default to Illinois if nothing found
1012-
return "illinois";
1013-
}
1014992
}
1015993

1016994
// Export for module use or make globally available

‎efile_app/efile/static/js/form-validation.js‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
/**
23
* FormValidation - Handles form validation and user interactions
34
* Features: Real-time validation, draft saving, submission handling, API caching
@@ -161,7 +162,7 @@ class FormValidation {
161162
// Collect form data and add friendly names
162163
const formData = this.collectFormData();
163164
const enhancedFormData = this.addFriendlyNames(formData);
164-
165+
const currentJurisdiction = apiUtils.getCurrentJurisdiction();
165166

166167
try {
167168
// Save case data to session via API
@@ -181,7 +182,7 @@ class FormValidation {
181182
this.showNotification("Case data saved successfully!", "success");
182183

183184

184-
window.location.replace("/upload/");
185+
window.location.replace(`/${currentJurisdiction}/upload/`);
185186
} else {
186187
const error = await response.json();
187188
console.error("Failed to save case data - Status:", response.status);

0 commit comments

Comments
 (0)