Search code examples
swiftbackendvaporvapor-fluent

Vapor how to transform EventLoopFuture<Type> to <Type>


I am implementing a delete route handler using Vapor Fluent.

For this handler, I wanted to verify that the user sending the product deletion request is the owner of the product, and Abort the request otherwise.

func deleteHandler(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
    let user = req.auth.get(User.self)
    return Product.find(req.parameters.get("productID"), on: req.db)
        .unwrap(or: Abort(.notFound))
        .flatMap { product in
            return product.$user.get(on: req.db).flatMapThrowing { owner in
                guard try user?.requireID() == owner.requireID() else {
                    throw Abort(.forbidden)
                }
                return try product.delete(on: req.db)
                    .transform(to: HTTPStatus.noContent) // error here
            }
        }
}

But Vapor throws an error at return try product.delete(on: req.db).transform(to: HTTPStatus.noContent) saying Cannot convert return expression of type 'EventLoopFuture<HTTPResponseStatus>' to return type 'HTTPStatus' (aka 'HTTPResponseStatus').

I tried chaining again using map({}), that did not help. Using wait()solves the error but introduces a runtime bug.

Thanks for any help!


Solution

  • The problem is that the .flatMapThrowing isn't throwing the future. requireID() is overkill in this context. If you replace the throw as follows and remove the Throwing as a result, it should work:

    func deleteHandler(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
        let user = req.auth.get(User.self)
        return Product.find(req.parameters.get("productID"), on: req.db)
            .unwrap(or: Abort(.notFound))
            .flatMap { product in
                return product.$user.get(on: req.db).flatMap { owner in
                    guard user?.id == owner.id else {
                        return request.eventLoop.makeFailedFuture( Abort(.forbidden))
                    }
                    return product.delete(on: req.db)
                        .transform(to: HTTPStatus.noContent)
                }
            }
    }
    

    I've removed the try from the delete later on. I'm guessing the compiler didn't report this as unnecessary because of the earlier error, but it isn't usually needed. My answer will fail spectacularly if it is!