In my user controller the following function compiles fine.
func patchUser(req: Request) throws -> EventLoopFuture<User.Public> {
let data = try req.content.decode(PatchUserData.self)
return User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound)).flatMap { user in
if let password = data.password {
user.password = password
}
return user.save(on: req.db).map {
user.convertToPublic()
}
}
}
But when I try to hash the password, replacing
user.password = password
with
user.password = try Bcrypt.hash(password)
I get the following compile error.
error: invalid conversion from throwing function of type ‘(Optional.WrappedType) throws → EventLoopFuture<User.Public>’ (aka ‘(User) throws → EventLoopFuture<User.Public>’) to non-throwing function type ‘(Optional.WrappedType) → EventLoopFuture<User.Public>’ (aka ‘(User) → EventLoopFuture<User.Public>’)
.unwrap(or: Abort(.notFound)).flatMap { user in
^
As the error says, you can’t throw inside a flatMap. If you need to throw in the closure of a flatMap use the new tryFlatMap instead (make sure you update your dependencies). The books approach is to wrap the throw inside a do/catch as tryFlatMap wasn’t introduced at the time of writing
@randyf Empty catch blocks are not a good programming practice so should definitely be avoided because of that no matter what. You can actually fix this in two different ways in Vapor:
Create a failed future that handles the error accordingly with makeFailedFuture(_:) like this:
func patchUser(req: Request) throws -> EventLoopFuture<User.Public> {
let data = try req.content.decode(PatchUserData.self)
return User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound)).flatMap { user in
if let password = data.password {
do {
user.password = try Bcrypt.hash(password)
} catch {
return req.eventLoop.makeFailedFuture(error)
}
}
return user.save(on: req.db).map {
user.convertToPublic()
}
}
}
Throw the error with tryFlatMap(_:) as follows:
func patchUser(req: Request) throws -> EventLoopFuture<User.Public> {
let data = try req.content.decode(PatchUserData.self)
return User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound)).tryflatMap { user in
if let password = data.password {
user.password = try Bcrypt.hash(password)
}
return user.save(on: req.db).map {
user.convertToPublic()
}
}
}
Please let me know if you have any questions or issues about the whole thing when you get a chance. Thank you!