Something like json-api may return JSON with many objects in it:
{
"links": {
"books.author": {
"href": "http://example.com/authors/{books.author}",
"type": "authors"
}
},
"books": [{
"id": "1",
"title": "When In Doubt, Roll",
"links": {
"author": "1"
}
}],
"linked": {
"authors": [{
"id": "1",
"name": "Bill Bruford"
}]
}
}
Normally we would fetch an Author
from http://example.com/people/{id}
.
However, if we already got this JSON for a book, we can reuse it.
Let’s write a Source
for an Author
, that will be able to use both AuthorEndpoint
and CompoundBookEndpoint
.
The first step is to understand that Source
can take 4 parameters:
We only need the first three to make it work.
The default endpoint is AuthorEndpoint
:
def needAuthor(id: String) =
Source[Author](AuthorEndpoint(id))(
...,
...
)
Next, we show how to load an Author
from both endpoints:
{
case e @ AuthorEndpoint(`id`) ⇒
// just load normally
Resolvable[A].fromEndpoint(e)
case e @ CompoundBookEndpoint(_) ⇒
// inside the data returned by the endpoint,
// try to find the author we are looking for
// if not found, the endpoint will be simply skipped
Resolvable[A].fromEndpointPath(e) { js ⇒
(js \ "linked" \ "authors").find(_ \ "id" == JsString(id)).get
}
}
Finally, we need to assign higher priority to the compound endpoint, to avoid unnecessary fetching of AuthorEndpoint
:
{
case CompoundBookEndpoint(_) ⇒ Seq(1)
case AuthorEndpoint(_) ⇒ Seq(0) // default
}
Assembling everything together, we get:
def needAuthor(id: String) =
Source[Author](AuthorEndpoint(id))({
case e @ AuthorEndpoint(`id`) ⇒
Resolvable[A].fromEndpoint(e)
case e @ CompoundBookEndpoint(_) ⇒
Resolvable[A].fromEndpointPath(e) { js ⇒
(js \ "linked" \ "authors").find(_ \ "id" == JsString(id)).get
}
}, {
case CompoundBookEndpoint(_) ⇒ Seq(1)
case AuthorEndpoint(_) ⇒ Seq(0) // default
})