Fix booking profile request parsing without JsonNode
All checks were successful
build-and-deploy / build-deploy (push) Successful in 54s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 54s
This commit is contained in:
@@ -53,7 +53,6 @@ import com.android.trisolarisserver.repo.room.RoomRepo
|
||||
import com.android.trisolarisserver.repo.room.RoomStayAuditLogRepo
|
||||
import com.android.trisolarisserver.repo.room.RoomStayRepo
|
||||
import com.android.trisolarisserver.security.MyPrincipal
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
@@ -643,12 +642,9 @@ class BookingFlow(
|
||||
@PathVariable propertyId: UUID,
|
||||
@PathVariable bookingId: UUID,
|
||||
@AuthenticationPrincipal principal: MyPrincipal?,
|
||||
@RequestBody request: JsonNode
|
||||
@RequestBody request: Map<String, Any?>
|
||||
) {
|
||||
requireRole(propertyAccess, propertyId, principal, Role.ADMIN, Role.MANAGER)
|
||||
if (!request.isObject) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid request body")
|
||||
}
|
||||
|
||||
val allowedFields = setOf(
|
||||
"transportMode",
|
||||
@@ -659,7 +655,7 @@ class BookingFlow(
|
||||
"toCity",
|
||||
"memberRelation"
|
||||
)
|
||||
val fieldNames = request.fieldNames().asSequence().toList()
|
||||
val fieldNames = request.keys.toList()
|
||||
val unknownFields = fieldNames.filter { it !in allowedFields }
|
||||
if (unknownFields.isNotEmpty()) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown fields: ${unknownFields.joinToString(",")}")
|
||||
@@ -670,34 +666,34 @@ class BookingFlow(
|
||||
|
||||
val booking = requireBooking(propertyId, bookingId)
|
||||
|
||||
if (request.has("transportMode")) {
|
||||
val transportNode = request.get("transportMode")
|
||||
booking.transportMode = parseNullableTransportMode(booking, transportNode)
|
||||
if (request.containsKey("transportMode")) {
|
||||
booking.transportMode = parseNullableTransportMode(booking, request["transportMode"])
|
||||
}
|
||||
if (request.has("fromCity")) {
|
||||
booking.fromCity = parseNullableText(request.get("fromCity"), "fromCity")
|
||||
if (request.containsKey("fromCity")) {
|
||||
booking.fromCity = parseNullableText(request["fromCity"], "fromCity")
|
||||
}
|
||||
if (request.has("toCity")) {
|
||||
booking.toCity = parseNullableText(request.get("toCity"), "toCity")
|
||||
if (request.containsKey("toCity")) {
|
||||
booking.toCity = parseNullableText(request["toCity"], "toCity")
|
||||
}
|
||||
if (request.has("memberRelation")) {
|
||||
booking.memberRelation = parseNullableMemberRelation(request.get("memberRelation"))
|
||||
if (request.containsKey("memberRelation")) {
|
||||
booking.memberRelation = parseNullableMemberRelation(request["memberRelation"])
|
||||
}
|
||||
|
||||
val hasGuestCountPatch = request.has("maleCount") || request.has("femaleCount") || request.has("childCount")
|
||||
val hasGuestCountPatch =
|
||||
request.containsKey("maleCount") || request.containsKey("femaleCount") || request.containsKey("childCount")
|
||||
if (hasGuestCountPatch) {
|
||||
val maleCount = if (request.has("maleCount")) {
|
||||
parseNullableNonNegativeInt(request.get("maleCount"), "maleCount")
|
||||
val maleCount = if (request.containsKey("maleCount")) {
|
||||
parseNullableNonNegativeInt(request["maleCount"], "maleCount")
|
||||
} else {
|
||||
booking.maleCount
|
||||
}
|
||||
val femaleCount = if (request.has("femaleCount")) {
|
||||
parseNullableNonNegativeInt(request.get("femaleCount"), "femaleCount")
|
||||
val femaleCount = if (request.containsKey("femaleCount")) {
|
||||
parseNullableNonNegativeInt(request["femaleCount"], "femaleCount")
|
||||
} else {
|
||||
booking.femaleCount
|
||||
}
|
||||
val childCount = if (request.has("childCount")) {
|
||||
parseNullableNonNegativeInt(request.get("childCount"), "childCount")
|
||||
val childCount = if (request.containsKey("childCount")) {
|
||||
parseNullableNonNegativeInt(request["childCount"], "childCount")
|
||||
} else {
|
||||
booking.childCount
|
||||
}
|
||||
@@ -1036,13 +1032,13 @@ class BookingFlow(
|
||||
|
||||
private fun parseNullableTransportMode(
|
||||
booking: com.android.trisolarisserver.models.booking.Booking,
|
||||
node: JsonNode?
|
||||
value: Any?
|
||||
): TransportMode? {
|
||||
if (node == null || node.isNull) return null
|
||||
if (!node.isTextual) {
|
||||
if (value == null) return null
|
||||
if (value !is String) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "transportMode must be string or null")
|
||||
}
|
||||
val raw = node.asText().trim()
|
||||
val raw = value.trim()
|
||||
if (raw.isBlank()) return null
|
||||
val mode = parseTransportMode(raw.uppercase())
|
||||
if (!isTransportModeAllowed(booking.property, mode)) {
|
||||
@@ -1060,34 +1056,41 @@ class BookingFlow(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseNullableMemberRelation(node: JsonNode?): MemberRelation? {
|
||||
if (node == null || node.isNull) return null
|
||||
if (!node.isTextual) {
|
||||
private fun parseNullableMemberRelation(value: Any?): MemberRelation? {
|
||||
if (value == null) return null
|
||||
if (value !is String) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "memberRelation must be string or null")
|
||||
}
|
||||
return parseMemberRelation(node.asText())
|
||||
return parseMemberRelation(value)
|
||||
}
|
||||
|
||||
private fun parseNullableText(node: JsonNode?, fieldName: String): String? {
|
||||
if (node == null || node.isNull) return null
|
||||
if (!node.isTextual) {
|
||||
private fun parseNullableText(value: Any?, fieldName: String): String? {
|
||||
if (value == null) return null
|
||||
if (value !is String) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "$fieldName must be string or null")
|
||||
}
|
||||
return node.asText().trim().ifBlank { null }
|
||||
return value.trim().ifBlank { null }
|
||||
}
|
||||
|
||||
private fun parseNullableNonNegativeInt(node: JsonNode?, fieldName: String): Int? {
|
||||
if (node == null || node.isNull) return null
|
||||
if (!node.isIntegralNumber) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "$fieldName must be integer or null")
|
||||
private fun parseNullableNonNegativeInt(value: Any?, fieldName: String): Int? {
|
||||
if (value == null) return null
|
||||
val intValue = when (value) {
|
||||
is Int -> value
|
||||
is Long -> value.toInt()
|
||||
is Number -> {
|
||||
val asDouble = value.toDouble()
|
||||
if (asDouble % 1.0 != 0.0) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "$fieldName must be integer or null")
|
||||
}
|
||||
asDouble.toInt()
|
||||
}
|
||||
else -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, "$fieldName must be integer or null")
|
||||
}
|
||||
val value = node.asInt()
|
||||
if (value < 0) {
|
||||
if (intValue < 0) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "$fieldName must be >= 0")
|
||||
}
|
||||
return value
|
||||
return intValue
|
||||
}
|
||||
|
||||
private fun parseRateSource(value: String?): RateSource? {
|
||||
if (value.isNullOrBlank()) return null
|
||||
return try {
|
||||
|
||||
Reference in New Issue
Block a user