Skip to content

Commit 4cbfac8

Browse files
authored
Handle json nulls in Update and Create (#23)
* Fix Create and Update request models * Handle json nulls in Update and Create * Minor changes
1 parent 313fc83 commit 4cbfac8

File tree

4 files changed

+53
-2
lines changed

4 files changed

+53
-2
lines changed

spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminCreateTable.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ object AdminCreateTable {
88
case class Response(id: String)
99

1010
implicit val adminCreateTableRequestFormat: Format[Request] = Format[Request](
11-
fjs = implicitly[Reads[Map[String, String]]].map(Request.apply),
11+
// We have to handle null as Options
12+
fjs = implicitly[Reads[Map[String, Option[String]]]]
13+
.map(handleMapWithOptionalValue)
14+
.map(Request.apply),
1215
tjs = Writes[Request](x => Json.toJson(x.data))
1316
)
1417

spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminUpdateTable.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ object AdminUpdateTable {
77
case class Response(id: String)
88

99
implicit val adminUpdateTableRequestFormat: Format[Request] = Format[Request](
10-
fjs = implicitly[Reads[Map[String, String]]].map(Request.apply),
10+
// We have to handle null as Options
11+
fjs = implicitly[Reads[Map[String, Option[String]]]]
12+
.map(handleMapWithOptionalValue)
13+
.map(Request.apply),
1114
tjs = Writes[Request](x => Json.toJson(x.data))
1215
)
1316

spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/package.scala

+12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package net.wiringbits.spra.api
33
import play.api.libs.json.*
44

55
import java.time.Instant
6+
import scala.language.implicitConversions
67

78
package object models {
89

@@ -18,4 +19,15 @@ package object models {
1819

1920
case class ErrorResponse(error: String)
2021
implicit val errorResponseFormat: Format[ErrorResponse] = Json.format[ErrorResponse]
22+
23+
implicit def optionFormat[T](using formatter: Format[T]): Format[Option[T]] = Format[Option[T]](
24+
fjs = Reads[Option[T]](_.validateOpt[T]),
25+
tjs = Writes[Option[T]] {
26+
case Some(value) => Json.toJson(value)
27+
case None => Json.toJson("")
28+
}
29+
)
30+
31+
def handleMapWithOptionalValue(map: Map[String, Option[String]]): Map[String, String] =
32+
map.collect { case (key, Some(value)) => key -> value }
2133
}

spra-play-server/src/test/scala/controllers/AdminControllerSpec.scala

+33
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,18 @@ class AdminControllerSpec extends PlayPostgresSpec with AdminUtils {
669669
}
670670
}
671671

672+
"don't fail if we send a null in an optional parameter" in withApiClient { client =>
673+
val json = """{"name":"wiringbits","email":null,"password":"wiringbits"}"""
674+
val path = s"/admin/tables/${usersSettings.tableName}"
675+
val response = POST(path, json).futureValue
676+
response.header.status mustBe 200
677+
678+
val responseMetadata =
679+
client.getTableMetadata(usersSettings.tableName, List("name", "ASC"), List(0, 9), "{}").futureValue
680+
681+
responseMetadata.head.nonEmpty mustBe true
682+
}
683+
672684
"return new user id" in withApiClient { implicit client =>
673685
val user = createUser.futureValue
674686
val response = client.getTableMetadata(usersSettings.tableName, List("name", "ASC"), List(0, 9), "{}").futureValue
@@ -788,6 +800,27 @@ class AdminControllerSpec extends PlayPostgresSpec with AdminUtils {
788800
}
789801
}
790802

803+
"don't fail if we send a null in an optional parameter" in withApiClient { client =>
804+
val name = "wiringbits"
805+
val email = "[email protected]"
806+
val password = "wiringbits"
807+
val request = AdminCreateTable.Request(Map("name" -> name, "email" -> email, "password" -> password))
808+
client.createItem("users", request).futureValue
809+
val responseMetadata1 =
810+
client.getTableMetadata(usersSettings.tableName, List("name", "ASC"), List(0, 9), "{}").futureValue
811+
val id = responseMetadata1.head("id")
812+
813+
val json = """{"email":null}"""
814+
val path = s"/admin/tables/${usersSettings.tableName}/$id"
815+
val response = PUT(path, json).futureValue
816+
response.header.status mustBe 200
817+
818+
val responseMetadata2 =
819+
client.getTableMetadata(usersSettings.tableName, List("name", "ASC"), List(0, 9), "{}").futureValue
820+
821+
responseMetadata2.head("email") mustBe ""
822+
}
823+
791824
"fail if the field in body doesn't exists" in withApiClient { client =>
792825
val request = AdminCreateTable.Request(
793826
Map("name" -> "wiringbits", "email" -> "[email protected]", "password" -> "wiringbits")

0 commit comments

Comments
 (0)