Add public country search API backed by country_reference
All checks were successful
build-and-deploy / build-deploy (push) Successful in 36s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 36s
This commit is contained in:
@@ -148,6 +148,32 @@ AUTH + SYSTEM APIS
|
|||||||
- 400 Bad Request (q too short)
|
- 400 Bad Request (q too short)
|
||||||
- 401 Unauthorized
|
- 401 Unauthorized
|
||||||
|
|
||||||
|
|
||||||
|
- Country search API is this one:
|
||||||
|
|
||||||
|
GET /geo/countries/search
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
|
||||||
|
- Searches countries from local country_reference table.
|
||||||
|
- Case-insensitive match on country name, official name, ISO alpha-2, and ISO alpha-3.
|
||||||
|
- Example: q=IND returns matches like India and related country names.
|
||||||
|
|
||||||
|
Request body:
|
||||||
|
|
||||||
|
- None.
|
||||||
|
|
||||||
|
Query params:
|
||||||
|
|
||||||
|
- q (required, minimum 3 characters)
|
||||||
|
- limit (optional, default 20, min 1, max 100)
|
||||||
|
|
||||||
|
- Allowed roles: Public endpoint.
|
||||||
|
|
||||||
|
Error Codes
|
||||||
|
|
||||||
|
- 400 Bad Request (q too short)
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
PROPERTY + USER APIS
|
PROPERTY + USER APIS
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.android.trisolarisserver.controller.system
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.repo.property.CountryReferenceRepo
|
||||||
|
import org.springframework.data.domain.PageRequest
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class CountrySearch(
|
||||||
|
private val countryReferenceRepo: CountryReferenceRepo
|
||||||
|
) {
|
||||||
|
@GetMapping("/geo/countries/search")
|
||||||
|
fun searchCountries(
|
||||||
|
@RequestParam("q") q: String,
|
||||||
|
@RequestParam("limit", required = false, defaultValue = "20") limit: Int
|
||||||
|
): List<CountrySearchResponse> {
|
||||||
|
val query = q.trim()
|
||||||
|
if (query.length < 3) {
|
||||||
|
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "q must be at least 3 characters")
|
||||||
|
}
|
||||||
|
val boundedLimit = limit.coerceIn(1, 100)
|
||||||
|
return countryReferenceRepo
|
||||||
|
.searchCountries(query, PageRequest.of(0, boundedLimit))
|
||||||
|
.map {
|
||||||
|
CountrySearchResponse(
|
||||||
|
name = it.name,
|
||||||
|
officialName = it.officialName,
|
||||||
|
isoAlpha2 = it.isoAlpha2,
|
||||||
|
isoAlpha3 = it.isoAlpha3
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CountrySearchResponse(
|
||||||
|
val name: String,
|
||||||
|
val officialName: String?,
|
||||||
|
val isoAlpha2: String?,
|
||||||
|
val isoAlpha3: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.android.trisolarisserver.models.property
|
||||||
|
|
||||||
|
import jakarta.persistence.Column
|
||||||
|
import jakarta.persistence.Entity
|
||||||
|
import jakarta.persistence.GeneratedValue
|
||||||
|
import jakarta.persistence.GenerationType
|
||||||
|
import jakarta.persistence.Id
|
||||||
|
import jakarta.persistence.Table
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "country_reference")
|
||||||
|
class CountryReference(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
val id: Long? = null,
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false)
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
@Column(name = "official_name")
|
||||||
|
val officialName: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "iso_alpha2")
|
||||||
|
val isoAlpha2: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "iso_alpha3")
|
||||||
|
val isoAlpha3: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "latitude", precision = 11, scale = 8)
|
||||||
|
val latitude: BigDecimal? = null,
|
||||||
|
|
||||||
|
@Column(name = "longitude", precision = 11, scale = 8)
|
||||||
|
val longitude: BigDecimal? = null,
|
||||||
|
|
||||||
|
@Column(name = "source_row_code")
|
||||||
|
val sourceRowCode: Int? = null,
|
||||||
|
|
||||||
|
@Column(name = "created_at", nullable = false, columnDefinition = "timestamptz")
|
||||||
|
val createdAt: OffsetDateTime = OffsetDateTime.now()
|
||||||
|
)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.android.trisolarisserver.repo.property
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.models.property.CountryReference
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
|
import org.springframework.data.repository.query.Param
|
||||||
|
|
||||||
|
interface CountryReferenceRepo : JpaRepository<CountryReference, Long> {
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
select c.name as name,
|
||||||
|
c.officialName as officialName,
|
||||||
|
c.isoAlpha2 as isoAlpha2,
|
||||||
|
c.isoAlpha3 as isoAlpha3
|
||||||
|
from CountryReference c
|
||||||
|
where trim(c.name) <> ''
|
||||||
|
and (
|
||||||
|
lower(c.name) like lower(concat('%', :query, '%'))
|
||||||
|
or lower(coalesce(c.officialName, '')) like lower(concat('%', :query, '%'))
|
||||||
|
or lower(coalesce(c.isoAlpha2, '')) like lower(concat('%', :query, '%'))
|
||||||
|
or lower(coalesce(c.isoAlpha3, '')) like lower(concat('%', :query, '%'))
|
||||||
|
)
|
||||||
|
order by
|
||||||
|
case
|
||||||
|
when lower(c.name) = lower(:query) then 0
|
||||||
|
when lower(coalesce(c.isoAlpha2, '')) = lower(:query) then 1
|
||||||
|
when lower(coalesce(c.isoAlpha3, '')) = lower(:query) then 2
|
||||||
|
when lower(c.name) like lower(concat(:query, '%')) then 3
|
||||||
|
else 4
|
||||||
|
end,
|
||||||
|
c.name asc
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun searchCountries(
|
||||||
|
@Param("query") query: String,
|
||||||
|
pageable: Pageable
|
||||||
|
): List<CountrySearchRow>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CountrySearchRow {
|
||||||
|
val name: String
|
||||||
|
val officialName: String?
|
||||||
|
val isoAlpha2: String?
|
||||||
|
val isoAlpha3: String?
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ internal object PublicEndpoints {
|
|||||||
private val razorpayReturn = Regex("^/properties/[^/]+/razorpay/return/(success|failure)$")
|
private val razorpayReturn = Regex("^/properties/[^/]+/razorpay/return/(success|failure)$")
|
||||||
private val guestDocumentFile = Regex("^/properties/[^/]+/guests/[^/]+/documents/[^/]+/file$")
|
private val guestDocumentFile = Regex("^/properties/[^/]+/guests/[^/]+/documents/[^/]+/file$")
|
||||||
private val cancellationPolicy = Regex("^/properties/[^/]+/cancellation-policy$")
|
private val cancellationPolicy = Regex("^/properties/[^/]+/cancellation-policy$")
|
||||||
|
private val countrySearch = Regex("^/geo/countries/search$")
|
||||||
|
|
||||||
fun isPublic(request: HttpServletRequest): Boolean {
|
fun isPublic(request: HttpServletRequest): Boolean {
|
||||||
val path = request.requestURI
|
val path = request.requestURI
|
||||||
@@ -34,5 +35,6 @@ internal object PublicEndpoints {
|
|||||||
|| iconPngFile.matches(path)
|
|| iconPngFile.matches(path)
|
||||||
|| guestDocumentFile.matches(path)
|
|| guestDocumentFile.matches(path)
|
||||||
|| (cancellationPolicy.matches(path) && method == "GET")
|
|| (cancellationPolicy.matches(path) && method == "GET")
|
||||||
|
|| (countrySearch.matches(path) && method == "GET")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user