diff --git a/src/main/kotlin/com/android/trisolarisserver/component/DataGovPincodeClient.kt b/src/main/kotlin/com/android/trisolarisserver/component/DataGovPincodeClient.kt index 8cbb024..58a4cf9 100644 --- a/src/main/kotlin/com/android/trisolarisserver/component/DataGovPincodeClient.kt +++ b/src/main/kotlin/com/android/trisolarisserver/component/DataGovPincodeClient.kt @@ -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() } diff --git a/src/main/kotlin/com/android/trisolarisserver/component/PostalPincodeClient.kt b/src/main/kotlin/com/android/trisolarisserver/component/PostalPincodeClient.kt index 9b53b4c..0e89dc1 100644 --- a/src/main/kotlin/com/android/trisolarisserver/component/PostalPincodeClient.kt +++ b/src/main/kotlin/com/android/trisolarisserver/component/PostalPincodeClient.kt @@ -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