Reputation: 1116
I have a Product
referencing a Store
model.
I'd like the response of /stores/1
to return a JSON that has also the referencing products. Something like:
{
data: {
storeName: "Store1",
id: 1
products: {
{ productName : "Product1", productPrice: 10},
{ productName : "Product2", productPrice: 100},
}
}
}
I'm currently stuck on my Yesod handler with getting the products injected in the right place.
getStoreR :: StoreId -> Handler Value
getStoreR storeId = do
store <- runDB $ get404 storeId
products <- runDB $ selectList [StoreId ==. storeId] []
let productsJson = [entityIdToJSON (Entity k r) | Entity k r <- products]
let storeJson = entityIdToJSON (Entity storeId store)
-- Inject productsJson under "products" property fails
let storeJsonWithProducts = HM.insert "products" productsJson storeJson
return $ object ["data" .= storeJsonWithProducts]
fails with:
Couldn't match expected type ‘HM.HashMap k0 [Value]’
with actual type ‘Value’
Relevant bindings include
storeJsonWithProducts :: HM.HashMap k0 [Value]
(bound at Main.hs:80:9)
In the third argument of ‘HM.insert’, namely ‘storeJson’
In the expression: HM.insert "products" productsJson storeJson
(btw, I have created a single file app with this here)
Upvotes: 1
Views: 144
Reputation: 2703
HashMap.insert
has the type k -> v -> HashMap k v -> HashMap k v
. Your storeJson
is not a HashMap
but a Value
—one made with an Object :: HashMap Text Value -> Value
constructor. This means v ~ Value
(in case you're not familiar, you can read ~
as type equality). However, this is then a problem since your productsJson
is not a Value
but actually a [Value]
.
Therefore, to solve your problem, you need to:
Extract the HashMap
from storeJson
.
let storeHM = case storeJson of
Object h -> h
You should ensure to handle other constructors here properly, of course, since this will crash if storeJson
isn't constructed with Object
.
Convert productJson
to a Value
. One of Value
's constructors is Array :: Vector Value -> Value
and you can get a Vector Value
from [Value]
using Data.Vector.fromList
:
import qualified Data.Vector as V
[...]
let productsValue = Array (V.fromList productsJson)
Finally, insert productsValue
into the storeHM
HashMap
:
let storeHMWithProducts = HM.insert "products" productsValue storeHM
You can then proceed as you did, using object
to convert storeHMWithProducts
into a JSON Value
again:
return $ object ["data" .= storeHMWithProducts]
Upvotes: 1