Search code examples
swiftxcodefluentvapor

Why is no pivot entry being created in vapor when this code runs?


Hope someone can see what I'm missing here. I'm trying to create a pivot table entry between users and tournaments, two tables in my app. As you'd expect, a user is authorized during the routing, which ends up in this method (I've tried creating the pivot from both sides, as the commented line indicates):

    func acceptInvitation(req: Request) -> EventLoopFuture<Response> {
        let user : User = req.auth.get(User.self)!
        let uId : UUID = user.id!
        let iId = UUID.init(uuidString: req.parameters.get("iId")!)
        return Invitation.query(on: req.db)
            .filter(\.$id == iId!)
            .with(\.$owner)
            .first().flatMap { i -> EventLoopFuture<Response> in
                i?.acceptances!.append((uId))
                i?.update(on: req.db)
                i!.owner.acceptances!.append(uId)
                i?.owner.update(on: req.db)
                i?.owner.$tournamentPlayers.attach(user, on: req.db)
//                user.$playerTournaments.attach(i!.owner, on: req.db)
                return req.eventLoop.future(req.redirect(to: "/")).encodeResponse(for: req)
            }
    }

The migration is fine, and postgres displays it as:

vapor-database=# \d tournament-player-pivot;
        Table "public.tournament-player-pivot"
    Column    | Type | Collation | Nullable | Default 
--------------+------+-----------+----------+---------
 id           | uuid |           | not null | 
 tournamentID | uuid |           | not null | 
 playerID     | uuid |           | not null | 
Indexes:
    "tournament-player-pivot_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "tournament-player-pivot_playerID_fkey" FOREIGN KEY ("playerID") REFERENCES users(id)
    "tournament-player-pivot_tournamentID_fkey" FOREIGN KEY ("tournamentID") REFERENCES tournament(id)

in Tournament, it's defined thusly:

    @Siblings(
      through: TournamentPlayerPivot.self,
      from: \.$tournament,
      to: \.$player)
    var tournamentPlayers: [User]

and the inverse, in Users is:

    @Siblings(
      through: TournamentPlayerPivot.self,
      from: \.$player,
      to: \.$tournament)
    var playerTournaments: [Tournament]

All of which seems to follow standard practice pretty closely. But when the method above is called (all the other code executes as expected), there's no entry in the table.

EDIT: I omitted the class for the pivot: here that is:

import Foundation
import Fluent

final class TournamentPlayerPivot: Model {
    static let schema = "tournament-player-pivot"

    @ID
    var id: UUID?
    @Parent(key: "tournamentID")        var tournament: Tournament
    @Parent(key: "playerID")              var player: User

    init() {}

    init(
        id: UUID? = nil,
        tournament: Tournament,
        player: User
        ) throws {
            self.id = id
            self.$tournament.id = try tournament.requireID()
            self.$player.id = try player.requireID()
        }
}

Solution

  • The two updates and the attach may be conflicting. I would try re-structuring your code as:

    func acceptInvitation(req: Request) -> EventLoopFuture<Response> {
        let user : User = req.auth.get(User.self)!
        let uId : UUID = user.id!
        let iId = UUID.init(uuidString: req.parameters.get("iId")!)
        return Invitation.query(on: req.db)
            .filter(\.$id == iId!)
            .with(\.$owner)
            .first().flatMap { i -> EventLoopFuture<Response> in
                i?.acceptances!.append((uId))
                i?.update(on: req.db).flatMap { _ in
                    i!.owner.acceptances!.append(uId)
                    i?.owner.update(on: req.db).flatMap { _ in
                        i?.owner.$tournamentPlayers.attach(user, on: req.db).flatMap { _ in
                            return req.eventLoop.future(req.redirect(to: "/")).encodeResponse(for: req)
                        }
                    }
                }
            }
    }
    

    I have definitely seen missing/unchanged data in the context of multiple updates where you don't map the future result and 'wait' for the earlier update(s) to happen. My guess is that something similar is happening with the attach. You may get some compiler groans and need to insert explicit return types.