diff --git a/app/controllers/OrderApiController.asp b/app/controllers/OrderApiController.asp index 3b2b7fa..9736d0c 100644 --- a/app/controllers/OrderApiController.asp +++ b/app/controllers/OrderApiController.asp @@ -146,9 +146,7 @@ Class OrderApiController_Class "
" & _ "" & _ "" & _ "
" & _ - "PE" & _ - "Purple Envelope" & _ - "
Addressing & Barcoding
" & _ + "KCI Purple Envelope Order Confirmation" & _ "
" & _ "
Order Received
" & _ @@ -182,14 +180,17 @@ Class OrderApiController_Class End If If IsTrue(model.BlueWantsQuote) Then + ' City/permit number are always collected for an own-organization permit - a + ' permit lookup can't be done from the nonprofit authorization code alone, so + ' that code is additional information when nonprofit status is Yes, not a + ' replacement for city/permit number. Dim bluePermitLine bluePermitLine = "" If LabelPermitOwnership(model.BluePermitOwnership) = "My organization's permit" Then + bluePermitLine = EmailRow("City of permit holder", model.BluePermitCity) & _ + EmailRow("Permit number", model.BluePermitNumber) If IsTrue(model.BlueHasNonprofitStatus) Then - bluePermitLine = EmailRow("Nonprofit authorization code", model.BlueNonprofitAuthCode) - Else - bluePermitLine = EmailRow("City of permit holder", model.BluePermitCity) & _ - EmailRow("Permit number", model.BluePermitNumber) + bluePermitLine = bluePermitLine & EmailRow("Nonprofit authorization code", model.BlueNonprofitAuthCode) End If End If diff --git a/app/models/JurisdictionValidator.asp b/app/models/JurisdictionValidator.asp index 0635cd4..83d98c0 100644 --- a/app/models/JurisdictionValidator.asp +++ b/app/models/JurisdictionValidator.asp @@ -2,9 +2,105 @@ '======================================================================================================================= ' Jurisdiction Number Validation '======================================================================================================================= -' STUB: always returns True. Replace with a real lookup once a jurisdictions -' table/reference list exists to validate the submitted number against. +' Validates a submitted jurisdiction number against the JCode field of the jurisdictions +' list served by JurisdictionApiUrl (see public/web.config). That list is essentially static +' reference data (townships/municipalities), so it's cached in Application scope for +' JurisdictionCacheMinutes (default 60) instead of being fetched on every /request-order +' submission. If a refresh attempt fails, stale cached data is used rather than blocking +' order requests; only when there is no cached data at all (e.g. immediately after an app +' pool restart, with the API also unreachable) does validation fail open (allow) rather than +' block every order request site-wide. +' +' The cache is stored as a single delimited STRING ("|code1|code2|...|"), not a +' Scripting.Dictionary or array of objects - Scripting.Dictionary is an apartment-threaded +' COM object, and IIS raises "ASP 0197: Disallowed object use" if you try to store one in +' the Application intrinsic (which is shared across all requests/threads). A plain string is +' just script data, so it's safe to cache this way. +'======================================================================================================================= + Function IsValidJurisdictionNumber(jurisdictionNumber) - IsValidJurisdictionNumber = True + Dim codesText + codesText = GetJurisdictionCodesText() + + jurisdictionNumber = Trim(jurisdictionNumber) + + If Len(codesText) = 0 Then + ' No jurisdiction list available at all (first request after startup, API also + ' down) - fail open rather than block every order request site-wide. + IsValidJurisdictionNumber = True + Else + IsValidJurisdictionNumber = (InStr(codesText, "|" & jurisdictionNumber & "|") > 0) + End If +End Function + +' Returns the cached "|code1|code2|...|" string, refreshing it from JurisdictionApiUrl when +' missing or older than JurisdictionCacheMinutes. Falls back to the existing (stale) cached +' string if a refresh attempt fails. +Private Function GetJurisdictionCodesText() + Dim cacheMinutes, cachedAt, isStale + + cacheMinutes = GetAppSetting("JurisdictionCacheMinutes") + If Not IsNumeric(cacheMinutes) Then cacheMinutes = 60 + + cachedAt = Application("JurisdictionCodesFetchedAt") + isStale = IsEmpty(cachedAt) + If Not isStale Then isStale = (DateDiff("n", cachedAt, Now()) >= CInt(cacheMinutes)) + + If isStale Then + Dim fresh + fresh = FetchJurisdictionCodesText() + If Len(fresh) > 0 Then + Application.Lock + Application("JurisdictionCodesText") = fresh + Application("JurisdictionCodesFetchedAt") = Now() + Application.Unlock + End If + End If + + GetJurisdictionCodesText = Application("JurisdictionCodesText") +End Function + +' Fetches the jurisdictions list and extracts every JCode value. Returns a +' "|code1|code2|...|" string, or "" on any failure (network, HTTP status, or no matches). +' +' Uses a targeted regex over the raw response text rather than this codebase's generic +' aspJSON parser (core/lib.json.asp) - that parser walks the input character-by-character in +' plain VBScript, which measured at over 40 seconds for this API's ~450KB/1500-record +' response. RegExp.Execute is implemented natively and handles the same payload +' near-instantly; since JCode is the only field this validator needs, a full generic parse +' isn't necessary anyway. +Private Function FetchJurisdictionCodesText() + Dim apiUrl : apiUrl = GetAppSetting("JurisdictionApiUrl") + Dim result : result = "" + + On Error Resume Next + + Dim http : Set http = Server.CreateObject("Msxml2.ServerXMLHTTP") + http.setTimeouts 5000, 5000, 5000, 5000 + http.Open "GET", apiUrl, False + http.Send "" + + If Err.Number = 0 And http.Status = 200 Then + Dim re, matches, m, codes, code + Set re = New RegExp + re.Pattern = """JCode""\s*:\s*""([^""]*)""" + re.Global = True + re.IgnoreCase = True + + Set matches = re.Execute(http.responseText) + If Err.Number = 0 And matches.Count > 0 Then + codes = "|" + For Each m In matches + code = Trim(m.SubMatches(0)) + If Len(code) > 0 Then codes = codes & code & "|" + Next + If Len(codes) > 1 Then result = codes + End If + End If + + Err.Clear + On Error GoTo 0 + + FetchJurisdictionCodesText = result End Function %> diff --git a/app/views/Order/continue.asp b/app/views/Order/continue.asp index d4c85a1..9b9622d 100644 --- a/app/views/Order/continue.asp +++ b/app/views/Order/continue.asp @@ -217,13 +217,13 @@ }, { type: "text", name: "BluePermitCity", title: "City of permit holder", - description: "Please provide the following information.", - visibleIf: "{BlueHasNonprofitStatus} = false", + description: "A permit lookup can't be done from the authorization code alone, so please also provide the following information.", + visibleIf: "{BluePermitOwnership} = 'OwnPermit'", isRequired: true }, { type: "text", name: "BluePermitNumber", title: "Permit Number", - visibleIf: "{BlueHasNonprofitStatus} = false", + visibleIf: "{BluePermitOwnership} = 'OwnPermit'", isRequired: true } ] diff --git a/public/web.config b/public/web.config index 4e40608..bd6a612 100644 --- a/public/web.config +++ b/public/web.config @@ -53,6 +53,14 @@ + + + +