Skip to content

Commit

Permalink
Merge pull request #672 from vapor/vapor-gm
Browse files Browse the repository at this point in the history
Update to Vapor 4 GM
  • Loading branch information
tanner0101 authored Apr 9, 2020
2 parents 7ba3e68 + 6ebd007 commit 4401a7e
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 162 deletions.
7 changes: 4 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import PackageDescription
let package = Package(
name: "fluent",
platforms: [
.macOS(.v10_15)
.macOS(.v10_15),
],
products: [
.library(name: "Fluent", targets: ["Fluent"]),
],
dependencies: [
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.0.0-rc.1"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-rc.1"),
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.0.0-rc.1.19"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
],
targets: [
.target(name: "Fluent", dependencies: [
Expand All @@ -20,6 +20,7 @@ let package = Package(
]),
.testTarget(name: "FluentTests", dependencies: [
.target(name: "Fluent"),
.product(name: "XCTFluent", package: "fluent-kit"),
.product(name: "XCTVapor", package: "vapor"),
]),
]
Expand Down
19 changes: 19 additions & 0 deletions Sources/Fluent/Deprecated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Vapor

@available(*, deprecated, renamed: "ModelAuthenticatable")
public typealias ModelUser = ModelAuthenticatable

@available(*, deprecated, renamed: "ModelAuthenticatable")
public typealias ModelUserToken = ModelTokenAuthenticatable

extension Application.Fluent.Sessions {
@available(*, deprecated, renamed: "Model.sessionAuthenticator()")
public func middleware<User>(
for user: User.Type,
databaseID: DatabaseID? = nil
) -> Middleware
where User: SessionAuthenticatable, User: Model, User.SessionID == User.IDValue
{
User.sessionAuthenticator(databaseID)
}
}
86 changes: 52 additions & 34 deletions Sources/Fluent/Fluent+Sessions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,28 @@ extension Application.Fluent {
}
}

extension Application.Fluent.Sessions {
public func middleware<User>(
for user: User.Type,
databaseID: DatabaseID? = nil
) -> Middleware
where User: SessionAuthenticatable, User: Model, User.SessionID == User.IDValue
{
DatabaseSessionAuthenticator<User>(databaseID: databaseID).middleware()
public protocol ModelSessionAuthenticatable: Model, SessionAuthenticatable
where Self.SessionID == Self.IDValue
{ }

extension ModelSessionAuthenticatable {
public var sessionID: SessionID {
guard let id = self.id else {
fatalError("Cannot persist unsaved model to session.")
}
return id
}
}

extension Model where Self: SessionAuthenticatable, Self.SessionID == Self.IDValue {
public static func sessionAuthenticator(
_ databaseID: DatabaseID? = nil
) -> Authenticator {
DatabaseSessionAuthenticator<Self>(databaseID: databaseID)
}
}

extension Application.Fluent.Sessions {
public func driver(_ databaseID: DatabaseID? = nil) -> SessionDriver {
DatabaseSessions(databaseID: databaseID)
}
Expand All @@ -46,28 +58,28 @@ private struct DatabaseSessions: SessionDriver {

func createSession(_ data: SessionData, for request: Request) -> EventLoopFuture<SessionID> {
let id = self.generateID()
return Session(key: id, data: data)
return SessionRecord(key: id, data: data)
.create(on: request.db(self.databaseID))
.map { id }
}

func readSession(_ sessionID: SessionID, for request: Request) -> EventLoopFuture<SessionData?> {
return Session.query(on: request.db(self.databaseID))
SessionRecord.query(on: request.db(self.databaseID))
.filter(\.$key == sessionID)
.first()
.map { $0?.data }
}

func updateSession(_ sessionID: SessionID, to data: SessionData, for request: Request) -> EventLoopFuture<SessionID> {
return Session.query(on: request.db(self.databaseID))
SessionRecord.query(on: request.db(self.databaseID))
.filter(\.$key == sessionID)
.set(\.$data, to: data)
.update()
.map { sessionID }
}

func deleteSession(_ sessionID: SessionID, for request: Request) -> EventLoopFuture<Void> {
return Session.query(on: request.db(self.databaseID))
SessionRecord.query(on: request.db(self.databaseID))
.filter(\.$key == sessionID)
.delete()
}
Expand All @@ -86,15 +98,37 @@ private struct DatabaseSessionAuthenticator<User>: SessionAuthenticator
{
let databaseID: DatabaseID?

func resolve(sessionID: User.SessionID, for request: Request) -> EventLoopFuture<User?> {
User.find(sessionID, on: request.db)
func authenticate(sessionID: User.SessionID, for request: Request) -> EventLoopFuture<Void> {
User.find(sessionID, on: request.db).map {
if let user = $0 {
request.auth.login(user)
}
}
}
}

public final class Session: Model {
public static let schema = "sessions"
public final class SessionRecord: Model {
public static let schema = "_fluent_sessions"

private struct _Migration: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema("_fluent_sessions")
.id()
.field("key", .string, .required)
.field("data", .json, .required)
.create()
}

func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("_fluent_sessions").delete()
}
}

public static var migration: Migration {
_Migration()
}

@ID(key: "id")
@ID(key: .id)
public var id: UUID?

@Field(key: "key")
Expand All @@ -103,27 +137,11 @@ public final class Session: Model {
@Field(key: "data")
public var data: SessionData

public init() {

}
public init() { }

public init(id: UUID? = nil, key: SessionID, data: SessionData) {
self.id = id
self.key = key
self.data = data
}
}

public struct CreateSession: Migration {
public func prepare(on database: Database) -> EventLoopFuture<Void> {
return database.schema("sessions")
.field("id", .uuid, .identifier(auto: false))
.field("key", .string, .required)
.field("data", .json, .required)
.create()
}

public func revert(on database: Database) -> EventLoopFuture<Void> {
return database.schema("sessions").delete()
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import Vapor

public protocol ModelUser: Model, Authenticatable {
public protocol ModelAuthenticatable: Model, Authenticatable {
static var usernameKey: KeyPath<Self, Field<String>> { get }
static var passwordHashKey: KeyPath<Self, Field<String>> { get }
func verify(password: String) throws -> Bool
}

extension ModelUser {
extension ModelAuthenticatable {
public static func authenticator(
database: DatabaseID? = nil
) -> ModelUserAuthenticator<Self> {
ModelUserAuthenticator<Self>(database: database)
) -> Authenticator {
ModelAuthenticator<Self>(database: database)
}

var _$username: Field<String> {
Expand All @@ -22,27 +22,27 @@ extension ModelUser {
}
}

public struct ModelUserAuthenticator<User>: BasicAuthenticator
where User: ModelUser
private struct ModelAuthenticator<User>: BasicAuthenticator
where User: ModelAuthenticatable
{
public let database: DatabaseID?

public func authenticate(
basic: BasicAuthorization,
for request: Request
) -> EventLoopFuture<User?> {
) -> EventLoopFuture<Void> {
User.query(on: request.db(self.database))
.filter(\._$username == basic.username)
.first()
.flatMapThrowing
{
guard let user = $0 else {
return nil
return
}
guard try user.verify(password: basic.password) else {
return nil
return
}
return user
request.auth.login(user)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import Vapor

public protocol ModelUserToken: Model {
public protocol ModelTokenAuthenticatable: Model, Authenticatable {
associatedtype User: Model & Authenticatable
static var valueKey: KeyPath<Self, Field<String>> { get }
static var userKey: KeyPath<Self, Parent<User>> { get }
var isValid: Bool { get }
}

extension ModelUserToken {
extension ModelTokenAuthenticatable {
public static func authenticator(
database: DatabaseID? = nil
) -> ModelUserTokenAuthenticator<Self> {
ModelUserTokenAuthenticator<Self>(database: database)
) -> Authenticator {
ModelTokenAuthenticator<Self>(database: database)
}

var _$value: Field<String> {
Expand All @@ -23,30 +23,32 @@ extension ModelUserToken {
}
}

public struct ModelUserTokenAuthenticator<Token>: BearerAuthenticator
where Token: ModelUserToken
private struct ModelTokenAuthenticator<Token>: BearerAuthenticator
where Token: ModelTokenAuthenticatable
{
public typealias User = Token.User
public let database: DatabaseID?

public func authenticate(
bearer: BearerAuthorization,
for request: Request
) -> EventLoopFuture<User?> {
) -> EventLoopFuture<Void> {
let db = request.db(self.database)
return Token.query(on: db)
.filter(\._$value == bearer.token)
.first()
.flatMap
{ token -> EventLoopFuture<User?> in
{ token -> EventLoopFuture<Void> in
guard let token = token else {
return request.eventLoop.makeSucceededFuture(nil)
return request.eventLoop.makeSucceededFuture(())
}
guard token.isValid else {
return token.delete(on: db).map { nil }
return token.delete(on: db)
}
request.auth.login(token)
return token._$user.get(on: db).map {
request.auth.login($0)
}
return token._$user.get(on: db)
.map { $0 }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Fluent
import Vapor
import XCTVapor

final class FluentOperatorTests: XCTestCase {
final class OperatorTests: XCTestCase {
func testCustomOperators() throws {
let db = DummyDatabase()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import Fluent
import Vapor
import XCTVapor
import XCTFluent

final class FluentPaginationTests: XCTestCase {
final class PaginationTests: XCTestCase {
func testPagination() throws {
let app = Application(.testing)
defer { app.shutdown() }

var rows: [TestRow] = []
var rows: [TestOutput] = []
for i in 1...1_000 {
rows.append(TestRow(data: ["id": i, "title": "Todo #\(i)"]))
rows.append(TestOutput([
"id": i,
"title": "Todo #\(i)"
]))
}

app.databases.use(TestDatabaseConfiguration { query in
let test = CallbackTestDatabase { query in
XCTAssertEqual(query.schema, "todos")
let result: [TestRow]
let result: [TestOutput]
if let limit = query.limits.first?.value, let offset = query.offsets.first?.value {
result = [TestRow](rows[min(offset, rows.count - 1)..<min(offset + limit, rows.count)])
result = [TestOutput](rows[min(offset, rows.count - 1)..<min(offset + limit, rows.count)])
} else {
result = rows
}

switch query.action {
case .aggregate(_):
return [TestRow(data: [.aggregate: rows.count])]
default:
return result
case .aggregate(_):
return [TestOutput([.aggregate: rows.count])]
default:
return result
}
}, as: .test)
}
app.databases.use(test.configuration, as: .test)

app.get("todos") { req -> EventLoopFuture<Page<Todo>> in
Todo.query(on: req.db).paginate(for: req)
Expand Down
Loading

0 comments on commit 4401a7e

Please sign in to comment.