Путаница в отношении карты и flatMap: невозможно вызвать flatMap со списком аргументов типа '(() -> EventLoopFuture)' - PullRequest
1 голос
/ 04 июня 2019

Я новичок в Swift, работаю в фреймворке Vapor и занимаюсь разработкой процесса регистрации, который требует нескольких последовательных шагов для работы. Я столкнулся с проблемой следующего кода, который Xcode указывает на ошибку в отмеченной строке:

func signup(_ req: Request) throws -> Future<RecordA> {
    return try req.content.decode(RecordACreation.self).flatMap(to: RecordACreation.self) { recordACreation in

        // Prevent a recordA email from being used twice.
        RecordA(on: req).filter(\.email == recordACreation).first().map(to: RecordA.self) { optionalExistingRecordAByEmail in
            if let _ = optionalExistingRecordAByEmail {
                throw Abort(.unprocessableEntity, reason: "RecordA email already exists")
            }

        }.flatMap { // Xcode indicates there's an error here.
            // Prevent an recordB email from being used twice.
            return RecordB.query(on: req).filter(\.email == recordACreation.recordBEmail).first().map(to: RecordA.self) { optionalExistingRecordBByEmail in
                if let _ = optionalExistingRecordBByEmail {
                    throw Abort(.unprocessableEntity, reason: "RecordB email already exists.")
                }
            }

        }.flatMap {
            // If the recordB's password could not be successfully hashed.
            guard let recordBPassword = try? BCrypt.hash(recordACreation.recordBPassword) else {
                throw Abort(.unprocessableEntity, reason: "Password was unhashed")
            }

            // Ensure a verification token is generated successfully.
            guard let optionalVerificationToken = try? VerificationTokenService.generate(), let verificationToken = optionalVerificationToken else {
                throw Abort(.unprocessableEntity, reason: "Verification token could not be generated.")
            }

            let recordA = RecordA(name: recordACreation.recordAName, email: recordACreation.recordAEmail)

            return req.transaction(on: .sqlite) { conn in
                // TODO: Send email on signup success.

                return recordA.save(on: conn).map(to: RecordA.self) { savedRecordA in
                    guard let recordAId = recordA.id else {
                        throw Abort(.unprocessableEntity, reason: "Verification token could not be generated.")
                    }

                    _ = RecordB(name: recordACreation.recordBName, email: recordACreation.recordBEmail, password: recordBPassword, recordAId: recordAId, verificationToken: verificationToken).save(on: conn)
                    return savedRecordA
                }
            }
        }
    }
}

Ошибка гласит:

Невозможно вызвать 'flatMap' со списком аргументов типа '(() -> EventLoopFuture)'

Я не совсем понимаю механизмы между соответствующим выбором map и flatMap, так что это может быть причиной проблемы здесь. Мысли?

1 Ответ

1 голос
/ 04 июня 2019

Проблема заключается в вызове map после запроса RecordA:

RecordA(on: req).filter(\.email == recordACreation).first().map(to: RecordA.self) { optionalExistingRecordAByEmail in
    if let _ = optionalExistingRecordAByEmail {
        throw Abort(.unprocessableEntity, reason: "RecordA email already exists")
    }
}

Вы передаете RecordA.self параметру to, который устанавливает тип возвращаемого значения для закрытия на RecordA. Вместо этого вы ничего не возвращаете. Вы должны удалить параметр to, и он должен работать:

RecordA(on: req).filter(\.email == recordACreation).first().map { optionalExistingRecordAByEmail in
    if let _ = optionalExistingRecordAByEmail {
        throw Abort(.unprocessableEntity, reason: "RecordA email already exists")
    }
}
...