/
SprayJsonFormat.scala
109 lines (93 loc) · 4.55 KB
/
SprayJsonFormat.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package pl.iterators.kebs_examples
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import org.apache.pekko.http.scaladsl.marshalling.ToResponseMarshallable
import org.apache.pekko.http.scaladsl.model.StatusCodes._
import org.apache.pekko.http.scaladsl.server.Directives._
import pl.iterators.kebs.instances.net.URIString
import pl.iterators.kebs.instances.util.UUIDString
import pl.iterators.kebs.json.KebsSpray
import spray.json._
import java.net.URI
import java.util.UUID
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
object SprayJsonFormat {
trait ThingsService {
def create(request: ThingCreateRequest): Future[ThingCreateResponse]
}
object BeforeKebs {
trait JsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
implicit val urlJsonFormat = new JsonFormat[URI] {
override def read(json: JsValue): URI = json match {
case JsString(uri) => Try(new URI(uri)).getOrElse(deserializationError("Invalid URI format"))
case _ => deserializationError("URI should be string")
}
override def write(obj: URI): JsValue = JsString(obj.toString)
}
implicit val uuidFormat = new JsonFormat[UUID] {
override def write(obj: UUID): JsValue = JsString(obj.toString)
override def read(json: JsValue): UUID = json match {
case JsString(uuid) => Try(UUID.fromString(uuid)).getOrElse(deserializationError("Expected UUID format"))
case _ => deserializationError("Expected UUID format")
}
}
}
object ThingProtocol extends JsonProtocol {
def jsonFlatFormat[P, T <: Product](construct: P => T)(implicit jw: JsonWriter[P], jr: JsonReader[P]): JsonFormat[T] =
new JsonFormat[T] {
override def read(json: JsValue): T = construct(jr.read(json))
override def write(obj: T): JsValue = jw.write(obj.productElement(0).asInstanceOf[P])
}
implicit val errorJsonFormat = jsonFormat1(Error.apply)
implicit val thingIdJsonFormat = jsonFlatFormat(ThingId.apply)
implicit val tagIdJsonFormat = jsonFlatFormat(TagId.apply)
implicit val thingNameJsonFormat = jsonFlatFormat(ThingName.apply)
implicit val thingDescriptionJsonFormat = jsonFlatFormat(ThingDescription.apply)
implicit val locationJsonFormat = jsonFormat2(Location.apply)
implicit val createThingRequestJsonFormat = jsonFormat5(ThingCreateRequest.apply)
implicit val thingJsonFormat = jsonFormat6(Thing.apply)
}
class ThingRouter(thingsService: ThingsService)(implicit ec: ExecutionContext) {
import ThingProtocol._
def createRoute = (post & pathEndOrSingleSlash & entity(as[ThingCreateRequest])) { request =>
complete {
thingsService.create(request).map[ToResponseMarshallable] {
case ThingCreateResponse.Created(thing) => Created -> thing
case ThingCreateResponse.AlreadyExists => Conflict -> Error("Already exists")
}
}
}
}
}
object AfterKebs {
object ThingProtocol extends DefaultJsonProtocol with SprayJsonSupport with KebsSpray with URIString with UUIDString
class ThingRouter(thingsService: ThingsService)(implicit ec: ExecutionContext) {
import ThingProtocol._
def createRoute = (post & pathEndOrSingleSlash & entity(as[ThingCreateRequest])) { request =>
complete {
thingsService.create(request).map[ToResponseMarshallable] {
case ThingCreateResponse.Created(thing) => Created -> thing
case ThingCreateResponse.AlreadyExists => Conflict -> Error("Already exists")
}
}
}
}
}
case class ThingId(uuid: UUID)
case class ThingName(name: String)
case class ThingDescription(description: String)
case class TagId(id: String)
case class Location(latitude: Double, longitude: Double)
case class Thing(id: ThingId, name: ThingName, description: ThingDescription, pictureUri: URI, tags: List[TagId], location: Location)
case class ThingCreateRequest(name: ThingName,
description: ThingDescription,
pictureUrl: Option[URI],
tags: List[TagId],
location: Location)
sealed abstract class ThingCreateResponse
object ThingCreateResponse {
case class Created(thing: Thing) extends ThingCreateResponse
case object AlreadyExists extends ThingCreateResponse
}
case class Error(message: String)
}