Harden pincode lookups and retry postal http
All checks were successful
build-and-deploy / build-deploy (push) Successful in 33s

This commit is contained in:
androidlover5842
2026-01-31 12:05:35 +05:30
parent 901247a920
commit d594e40051
2 changed files with 60 additions and 21 deletions

View File

@@ -22,29 +22,45 @@ class DataGovPincodeClient(
fun resolve(pinCode: String): PincodeLookupResult {
if (apiKey.isBlank()) return PincodeLookupResult(null, null, "NO_API_KEY", "data.gov.in", "Missing API key")
return try {
val url = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("api-key", apiKey)
.queryParam("format", "json")
.queryParam("filters[pincode]", pinCode)
.toUriString()
val response = restTemplate.getForEntity(url, String::class.java)
val body = response.body ?: return PincodeLookupResult(null, null, "EMPTY_BODY", "data.gov.in")
val resolved = parseCityState(body, pinCode)
val status = if (resolved == null) "ZERO_RESULTS" else "OK"
PincodeLookupResult(resolved, body, status, "data.gov.in")
val first = fetch(pinCode)
if (first.resolvedCityState != null) return first
val second = fetch("${pinCode}.0")
if (second.resolvedCityState != null) return second
// Prefer more explicit mismatch status if records exist but don't match pin.
if (first.status == "FILTER_MISMATCH") return first
if (second.status == "FILTER_MISMATCH") return second
first
} catch (ex: Exception) {
logger.warn("Data.gov.in lookup failed: {}", ex.message)
PincodeLookupResult(null, null, "ERROR", "data.gov.in", ex.message)
}
}
private fun parseCityState(body: String, pinCode: String): String? {
private fun fetch(pinCodeValue: String): PincodeLookupResult {
val url = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("api-key", apiKey)
.queryParam("format", "json")
.queryParam("filters[pincode]", pinCodeValue)
.toUriString()
val response = restTemplate.getForEntity(url, String::class.java)
val body = response.body ?: return PincodeLookupResult(null, null, "EMPTY_BODY", "data.gov.in")
val parsed = parseCityState(body, pinCodeValue)
val status = when {
parsed != null -> "OK"
isFilterMismatch(body, pinCodeValue) -> "FILTER_MISMATCH"
else -> "ZERO_RESULTS"
}
val error = if (status == "FILTER_MISMATCH") "Records did not match pin filter" else null
return PincodeLookupResult(parsed, body, status, "data.gov.in", error)
}
private fun parseCityState(body: String, pinCodeValue: String): String? {
val root = objectMapper.readTree(body)
val records = root.path("records")
if (!records.isArray || records.isEmpty) return null
val filtered = records.filter { record ->
val recordPin = record.path("pincode").asText(null)
recordPin?.trim() == pinCode
recordPin?.trim() == pinCodeValue
}
if (filtered.isEmpty()) return null
val chosen = chooseRecord(filtered) ?: return null
@@ -61,6 +77,17 @@ class DataGovPincodeClient(
return delivery ?: records.firstOrNull()
}
private fun isFilterMismatch(body: String, pinCodeValue: String): Boolean {
val root = objectMapper.readTree(body)
val records = root.path("records")
if (!records.isArray || records.isEmpty) return false
val anyMatch = records.any { record ->
val recordPin = record.path("pincode").asText(null)
recordPin?.trim() == pinCodeValue
}
return !anyMatch
}
private fun toTitleCase(value: String): String {
return value.lowercase().split(Regex("\\s+")).joinToString(" ") { word ->
word.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }

View File

@@ -19,21 +19,33 @@ class PostalPincodeClient(
fun resolve(pinCode: String): PincodeLookupResult {
return try {
val url = UriComponentsBuilder.fromUriString(baseUrl)
.path("/pincode/{pin}")
.buildAndExpand(pinCode)
.toUriString()
val response = restTemplate.getForEntity(url, String::class.java)
val body = response.body ?: return PincodeLookupResult(null, null, "EMPTY_BODY", "postalpincode.in")
val resolved = parseCityState(body)
val status = if (resolved == null) "ZERO_RESULTS" else "OK"
PincodeLookupResult(resolved, body, status, "postalpincode.in")
val first = fetch(baseUrl, pinCode)
if (first.resolvedCityState != null) return first
if (first.status == "ERROR" && baseUrl.startsWith("https://")) {
val httpUrl = baseUrl.replaceFirst("https://", "http://")
val second = fetch(httpUrl, pinCode)
if (second.resolvedCityState != null) return second
return second
}
first
} catch (ex: Exception) {
logger.warn("Postalpincode lookup failed: {}", ex.message)
PincodeLookupResult(null, null, "ERROR", "postalpincode.in", ex.message)
}
}
private fun fetch(base: String, pinCode: String): PincodeLookupResult {
val url = UriComponentsBuilder.fromUriString(base)
.path("/pincode/{pin}")
.buildAndExpand(pinCode)
.toUriString()
val response = restTemplate.getForEntity(url, String::class.java)
val body = response.body ?: return PincodeLookupResult(null, null, "EMPTY_BODY", "postalpincode.in")
val resolved = parseCityState(body)
val status = if (resolved == null) "ZERO_RESULTS" else "OK"
return PincodeLookupResult(resolved, body, status, "postalpincode.in")
}
private fun parseCityState(body: String): String? {
val root = objectMapper.readTree(body)
if (!root.isArray || root.isEmpty) return null