I'm trying to use persistent-postgresql with servant.
I have a User model.
I want to have an endpoint that takes an id and returns the user with that id.
According to other answers I can use toSqlKey to turn an Int64 into a Key to feed to get.
My function looks like:
oneUser :: Int64 -> App (Entity User)
oneUser userId = do
maybeUser <- runDb $ get $ toSqlKey userId
case maybeUser of
Nothing ->
throwError err404
Just user ->
return user
When I try to compile I get the error Couldn't match expected type ‘PersistEntityBackend (Entity User)’ with actual type ‘SqlBackend’
Use of selectList works fine.
allUsers :: App [Entity User]
allUsers = runDb $ selectList [] []
Please tell me what I'm doing wrong and where I should look in the future for stuff like this. I couldn't find get on hackage/the right version of the library on stackage etc.
runDb looks like:
runDb :: (MonadReader Config m, MonadIO m) => SqlPersistT IO b -> m b
runDb query = do
pool <- asks getPool
liftIO $ runSqlPool query pool
taken from this github project.
The difference is that get ... returns a plain User not an Entity User, so this will work:
altSingleUser :: Int64 -> App User
altSingleUser userid = do
let foo = get (toSqlKey userid) :: SqlPersistT IO (Maybe User)
maybeUser <- runDb $ foo
case maybeUser of
Nothing ->
throwError err404
Just person ->
return person
If you want to return an Entity User, just change the last return statement to:
return $ Entity { entityKey = toSqlKey userid, entityVal = person }
Related
I just start coding in Haskell recently and I start getting use of do block. I'm coming from Scala world, and after read a book and some blogs I see that do block was the inspiration of our from comprehension. But still I´m struggling with the arguments that pass in every function as input -> output
Here in my code I'm using scotty http server library to get a request and I'm trying to persist that request in mySQL.
But in this line where I try top get the value from the Maybe to send to the another function to be persisted
user <- (fmap (\user -> insertUser user) maybeUser)
It never compile since the function does not return the expected type
ActionT Text IO (Maybe User)
but
IO User
Here my hole program
createUser :: ActionM ()
createUser = do maybeUser <- getUserParam
-- Persist the user
_ <- persistUser
json maybeUser
getUserParam :: ActionT Text IO (Maybe User)
getUserParam = do requestBody <- body
return (decode requestBody)
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
persistUser _maybeUser = let maybeUser = _maybeUser in do
user <- maybeUser
user <- (fmap (\user -> insertUser user) maybeUser)
return maybeUser
insertUser :: User -> IO User
insertUser _user = let user = _user in do
conn <- createConnection
status <- execute conn insertUserQuery [MySQLInt32 (intToInt32 $ getUserId user), MySQLText "hello_haskell_world"]
return user
Let's consider the following function:
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
A value of type Maybe User is passed as an argument, and we need this user to be inserted into database. In order to do that, we can use (<$>) (or fmap) function as:
insertUser <$> maybeUser
The resulting type is: Maybe (IO User). Now we need to lift this type to ActionT Text IO (Maybe User) somehow.
Web.Scotty.Trans has a liftAndCatchIO function (also available in Web.Scotty module), which mostly does what we need, but it accepts IO a as an argument, so we need to "swap" Maybe and IO. Let's find a function for this. So sequence does what we need.
As a result, we have the following implementation of persistUser function:
persistUser maybeUser =
liftAndCatchIO $ sequence $ insertUser <$> maybeUser
Hi I have a piece of code where I retrieving a Maybe User where User is my own type.
getUserById :: Int -> IO (Maybe User)
getUserById id = let userId = id in do
conn <- createConnection
(columnDef, inputStream) <- query conn selectByIdQuery [One $ MySQLInt32 (intToInt32 userId)]
maybeMySQLValue <- Streams.read inputStream
return (transformToUser <$> maybeMySQLValue)
But the key is that the function that call this functions expect a IO User and not a IO Maybe User
getUserById :: Int -> IO User
Any advice about how to extract in the do block the value from the Maybe?
I´m trying this but still does not work
user <- extractMaybeUser (transformToUser <$> maybeMySQLValue)
return user
extractMaybeUser :: Maybe User -> User
extractMaybeUser maybeUser = case maybeUser of
Just value -> value
Nothing -> User 1 "default User"
Regards
user <- extractMaybeUser (transformToUser <$> maybeMySQLValue)
The reason why this code might not work is that extractMaybeUser has return type of User:
extractMaybeUser :: Maybe User -> User
There is no monadic value to extract from using <-, so what about just wrapping in IO the user we get from extractMaybeUser?
return (extractMaybeUser (transformToUser <$> maybeMySQLValue))
It will at least have IO User type
Having the following database models, I am trying to get the city name (a certain field from city - a related entity) of an event :
City json
name Text
countryId CountryId
UniqueCity name
deriving Eq
deriving Show
Event
title Text
description Text
date UTCTime
cityId CityId
extractCityName :: EventId -> Text
extractCityName eventId = do
event <- runDB $ get404 eventId
city <- runDB $ get404 (eventCityId event)
x <- cityName city
return cityName
And I am having this error even if the function is not called yet(at compile time):
Couldn't match expected type `HandlerT site0 IO t0' with actual type `Text'
In a stmt of a 'do' block: x <- cityName city
In the expression:
do { event <- runDB $ get404 eventId;
city <- runDB $ get404 (eventCityId event);
x <- cityName city;
return cityName }
Can you please help me figure out what is wrong with my code?
That's because the type signature of runDB is YesodDB site a -> HandlerT site IO a. Unless you want to run database related actions in yesod handers, you won't be needing runDB. The code for extracting city name would be like this:
extractCityName :: EventId -> ReaderT SqlBackend IO (Maybe Text)
extractCityName eventId = do
event <- selectFirst [EventId ==. eventId] [LimitTo 1]
case event of
Nothing -> return Nothing
Just event' -> do
city <- getJust (eventCityId $ entityVal event')
return $ Just $ cityName city
Now you can use the above function in the Yesod handlers when you actually need it. You can do the pattern matching in the result and send a 404 page if the result is Nothing.
I am working on a Servant 0.7.1 application and trying to use Persistent-2.5 to query a Postgresql database, but I am getting mismatched types with a Persistent query.
This application was previously working with Servant 0.4 and Persistent 2.2, but when I went to Servant 0.7.1 in order to try out the BasicAuth stuff (different stack resolver, which is why I ended up with a higher version of Persistent too), I changed from EitherT ServantErr IO to Servant's Handler monad, and for some reason I could no longer get the Persistent query to compile.
Here's my model definition:
share [mkPersist sqlSettings] [persistLowerCase|
ESeries json
label String
name String Maybe
relatedId ESeriesId Maybe
|]
Based on this blog-post, I have a runDb function that looks like this, which will run inside a ReaderT:
runDb query = do
pool <- asks getPool
liftIO $ runSqlPool query pool
Finally, I have the following api definition and handler:
type ESeriesApi = "series" :> Get '[JSON] [ESeries]
eSeriesApi :: Proxy ESeriesApi
eSeriesApi = Proxy
type AppM = ReaderT Config Handler
readerToHandler :: Config -> AppM :~> Handler
readerToHandler cfg = Nat $ \x -> runReaderT x cfg
eServer :: Config -> Server ESeriesApi
eServer cfg = enter (readerToHandler cfg) eSeriesServer
app :: Config -> Application
app cfg = serve eSeriesApi (eServer cfg)
eSeriesServer :: ServerT ESeriesApi AppM
eSeriesServer = allSeries
allSeries :: AppM [ESeries]
allSeries = do
series <- runDb $ selectList [] []
let results = map (\(Entity _ e) -> e) series
liftIO $ sequence results
I have tried many different variations on this, but it always comes down to the same error:
• Couldn't match type ‘persistent-2.5:Database.Persist.Class.PersistEntity.PersistEntityBackend
(IO ESeries)’
with ‘Database.Persist.Sql.Types.Internal.SqlBackend’
arising from a use of ‘selectList’
• In the second argument of ‘($)’, namely ‘selectList [] []’
In a stmt of a 'do' block: series <- runDb $ selectList [] []
In the expression:
do { series <- runDb $ selectList [] [];
let results = map (\ (Entity _ e) -> ...) series;
liftIO $ sequence results }
It seems that selectList is not returning the right type?
Edit:
I should have mentioned that I am trying to do this with Persistent 2.5 and this code previously worked with earlier versions of Persistent.
It looks like runSqlPool is expecting SqlPersistT or ReaderT SqlBackend but selectList is returning PersistEntityBackend (IO ESeries)
I want get the attribute value from Persist Entity; so i have the following code
userToText userId = do
user <- runDB $ get404 userId
userName user
This code doesn't compile, so I wrote those alternative version
userToText userId = do
user <- runDB $ get404 userId
listToJSON [user]
userToText userId = do
(_,_,_,_,_,name,_,_) <- runDB $ get404 userId
show name
All generate the same error
Handler/Report.hs:105:9:
Couldn't match expected type ‘HandlerT site IO b’
with actual type ‘Text’
Relevant bindings include
userToText :: Key PersistValue -> HandlerT site IO b
(bound at Handler/Report.hs:102:1)
In a stmt of a 'do' block: listToJSON [user]
In the expression:
do { user <- runDB $ get404 userId;
listToJSON [user] }
thanks for the help
Your code is in the Handler monad, so your function needs to return something of the type Handler Text rather than just Text:
userToText :: UserId -> Handler Text
userToText userId = do
user <- runDB $ get404 userId
return $ userName user -- Note the `return` here
(This is similar to how functions like getLine have type IO String rather than String).