Skip to content

Commit

Permalink
Implement Table.query(on:)
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaelIsaev committed May 19, 2020
1 parent db3b5b5 commit d36e5fd
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 1 deletion.
47 changes: 47 additions & 0 deletions Sources/Bridges/BridgesRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// BridgesRow.swift
// Bridges
//
// Created by Mihael Isaev on 18.05.2020.
//

import Foundation

public protocol BridgesRow {
func decode<D>(model type: D.Type) throws -> D where D: Decodable
func decode<D>(model type: D.Type, prefix: String?) throws -> D where D: Decodable
}

extension BridgesRow {
public func decode<D>(model type: D.Type) throws -> D where D : Decodable {
try decode(model: type, prefix: nil)
}
}

public protocol BridgesRows {
var rows: [BridgesRow] { get }
}

extension BridgesRows {
public func first<R>(as type: R.Type) throws -> R? where R: Decodable {
try rows.first?.decode(model: type)
}

public func all<R>(as type: R.Type) throws -> [R] where R: Decodable {
try rows.map { try $0.decode(model: type) }
}
}

extension EventLoopFuture where Value: BridgesRows {
public func first<R>(decoding type: R.Type) -> EventLoopFuture<R?> where R: Decodable {
flatMapThrowing { try $0.first(as: type) }
}

public func all<R>(decoding type: R.Type) -> EventLoopFuture<[R]> where R: Decodable {
flatMapThrowing { try $0.all(as: type) }
}
}

extension Array: BridgesRows where Element: BridgesRow {
public var rows: [BridgesRow] { self }
}
105 changes: 104 additions & 1 deletion Sources/Bridges/Extensions/Table+Conveniences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import NIO
import SwifQL

extension Table {
private static func error(_ logger: Logger) {
fileprivate static func error(_ logger: Logger) {
logger.error(.init(stringLiteral: "Query doesn't work with non-generic database identifier. Please initialize AnyDatabaseIdentifier as MySQL or Postgres explicitly."))
}

Expand All @@ -29,4 +29,107 @@ extension Table {
}
return db.first(Self.self, on: on)
}

public static func query(_ db: DatabaseIdentifier, on container: AnyBridgesObject) -> TableQuerySingle<Self> {
.init(db: db, container: container)
}

public static func query(on conn: BridgeConnection) -> TableQueryOnConn<Self> {
.init(conn)
}
}

struct CountResult: Aliasable {
@Alias("count")
var count: Int64
}

public class TableQuerySingle<T: Table>: QueryBuilderable {
let db: AnyDatabaseIdentifiable
let container: AnyBridgesObject

public var queryParts = QueryParts()

private init (db: AnyDatabaseIdentifiable, container: AnyBridgesObject) {
self.db = db
self.container = container
}

init (db: DatabaseIdentifier, container: AnyBridgesObject) {
guard let db = db as? AnyDatabaseIdentifiable else {
T.error(container.logger)
fatalError()
}
self.db = db
self.container = container
}

public func copy() -> TableQuerySingle<T> {
let copy = TableQuerySingle<T>(db: db, container: container)

copy.queryParts = queryParts.copy()

return copy
}

public func all() -> EventLoopFuture<[T]> {
let query = SwifQL.select(T.table.*).from(T.table)
return db.query(queryParts.appended(to: query), on: container)
.flatMapThrowing { try $0.map { try $0.decode(model: T.self) } }
}

public func count() -> EventLoopFuture<Int64> {
let query = SwifQL.select(Fn.count(T.table.*) => \CountResult.$count).from(T.table)
return db.query(queryParts.appended(to: query), on: container)
.flatMapThrowing { try $0.first?.decode(model: CountResult.self).count ?? 0 }
}

public func first() -> EventLoopFuture<T?> {
let query = SwifQL.select(T.table.*).from(T.table)
return db.query(queryParts.appended(to: query), on: container)
.flatMapThrowing { try $0.first?.decode(model: T.self) }
}

public func delete() -> EventLoopFuture<Void> {
let query = SwifQL.delete(from: T.table)
return db.query(queryParts.appended(to: query), on: container).transform(to: ())
}
}

public class TableQueryOnConn<T: Table>: QueryBuilderable {
let conn: BridgeConnection

public var queryParts = QueryParts()

init (_ conn: BridgeConnection) {
self.conn = conn
}

public func copy() -> TableQueryOnConn<T> {
let copy = TableQueryOnConn<T>(conn)

copy.queryParts = queryParts.copy()

return copy
}

public func all() -> EventLoopFuture<[T]> {
let query = SwifQL.select(T.table.*).from(T.table)
return conn.query(sql: queryParts.appended(to: query), decoding: T.self)
}

public func count() -> EventLoopFuture<Int64> {
let query = SwifQL.select(Fn.count(T.table.*) => \CountResult.$count).from(T.table)
return conn.query(sql: queryParts.appended(to: query), decoding: CountResult.self).map { $0.first?.count ?? 0 }
}

public func first() -> EventLoopFuture<T?> {
let query = SwifQL.select(T.table.*).from(T.table)
return conn.query(sql: queryParts.appended(to: query), decoding: T.self).map { $0.first }
}

public func delete() -> EventLoopFuture<Void> {
let query = SwifQL.delete(from: T.table)
return conn.query(sql: queryParts.appended(to: query), decoding: T.self).transform(to: ())
}
}
1 change: 1 addition & 0 deletions Sources/Bridges/Protocols/AnyDatabaseIdentifiable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import NIO
public protocol AnyDatabaseIdentifiable {
func all<T>(_ table: T.Type, on bridges: AnyBridgesObject) -> EventLoopFuture<[T]> where T: Table
func first<T>(_ table: T.Type, on bridges: AnyBridgesObject) -> EventLoopFuture<T?> where T: Table
func query(_ query: SwifQLable, on bridges: AnyBridgesObject) -> EventLoopFuture<[BridgesRow]>
}
public protocol AnyMySQLDatabaseIdentifiable: AnyDatabaseIdentifiable {}
public protocol AnyPostgresDatabaseIdentifiable: AnyDatabaseIdentifiable {}
Expand Down

0 comments on commit d36e5fd

Please sign in to comment.