Skip to content

Commit

Permalink
Add more options in findAll
Browse files Browse the repository at this point in the history
This commit adds in options:
* ORDER BY clause for sorting
* OFFSET and LIMIT for retrieving a portion of the rows

This builds the appropriate SQL queries for the options listed above.
This commit also adds in initial tests for the new options.

Signed-off-by: AnthonyAmanse <[email protected]>
  • Loading branch information
AnthonyAmanse committed Jun 22, 2018
1 parent 9d9618d commit 3c72ff7
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 11 deletions.
48 changes: 39 additions & 9 deletions Sources/SwiftKueryORM/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ public protocol Model: Codable {

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed an array of models or an error
static func findAll(using db: Database?, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void)
static func findAll(using db: Database?, orderBy: OrderBy..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void)

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed an array of tuples (id, model) or an error
static func findAll<I: Identifier>(using db: Database?, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void)
static func findAll<I: Identifier>(using db: Database?, orderBy: OrderBy..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void)

/// Call to find all the models in the database that accepts a completion
/// handler. The callback is passed a dictionary [id: model] or an error
static func findAll<I: Identifier>(using db: Database?, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void)
static func findAll<I: Identifier>(using db: Database?, orderBy: OrderBy..., offset: Int?, limit: Int?, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void)

/// Call to find all the models in the database matching the QueryParams that accepts a completion
/// handler. The callback is passed an array of models or an error
Expand Down Expand Up @@ -978,7 +978,7 @@ public extension Model {


///
static func findAll(using db: Database? = nil, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void) {
static func findAll(using db: Database? = nil, orderBy: OrderBy..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([Self]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
Expand All @@ -987,14 +987,24 @@ public extension Model {
return
}

let query = Select(from: table)
var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

Self.executeQuery(query: query, using: db, onCompletion)
}

/// Find all the models
/// - Parameter using: Optional Database to use
/// - Returns: An array of tuples (id, model)
static func findAll<I: Identifier>(using db: Database? = nil, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void) {
static func findAll<I: Identifier>(using db: Database? = nil, orderBy: OrderBy..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([(I, Self)]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
Expand All @@ -1003,12 +1013,22 @@ public extension Model {
return
}

let query = Select(from: table)
var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

Self.executeQuery(query: query, using: db, onCompletion)
}

/// :nodoc:
static func findAll<I: Identifier>(using db: Database? = nil, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void) {
static func findAll<I: Identifier>(using db: Database? = nil, orderBy: OrderBy..., offset: Int? = nil, limit: Int? = nil, _ onCompletion: @escaping ([I: Self]?, RequestError?) -> Void) {
var table: Table
do {
table = try Self.getTable()
Expand All @@ -1017,7 +1037,17 @@ public extension Model {
return
}

let query = Select(from: table)
var query = Select(from: table)
if orderBy.count > 0 {
query = query.order(by: orderBy)
}
if let offset = offset {
query = query.offset(offset)
}
if let limit = limit {
query = query.limit(to: limit)
}

Self.executeQuery(query: query, using: db) { (tuples: [(I, Self)]?, error: RequestError?) in
if let error = error {
onCompletion(nil, error)
Expand Down
17 changes: 15 additions & 2 deletions Tests/SwiftKueryORMTests/CommonUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class TestConnection: Connection {
case returnEmpty
case returnOneRow
case returnThreeRows
case returnThreeRowsSortedAscending
case returnThreeRowsSortedDescending
case returnError
case returnValue
}
Expand Down Expand Up @@ -100,6 +102,10 @@ class TestConnection: Connection {
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 1))))
case .returnThreeRows:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3))))
case .returnThreeRowsSortedAscending:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3, sortedByAge: "ascending"))))
case .returnThreeRowsSortedDescending:
onCompletion(.resultSet(ResultSet(TestResultFetcher(numberOfRows: 3, sortedByAge: "descending"))))
case .returnError:
onCompletion(.error(QueryError.noResult("Error in query execution.")))
case .returnValue:
Expand Down Expand Up @@ -136,12 +142,19 @@ class TestConnection: Connection {

class TestResultFetcher: ResultFetcher {
let numberOfRows: Int
let rows = [[1, "Joe", Int32(38)], [2, "Adam", Int32(28)], [3, "Chris", Int32(36)]]
var rows = [[1, "Joe", Int32(38)], [2, "Adam", Int32(28)], [3, "Chris", Int32(36)]]
let titles = ["id", "name", "age"]
var fetched = 0

init(numberOfRows: Int) {
init(numberOfRows: Int, sortedByAge: String? = nil) {
self.numberOfRows = numberOfRows
if let sortedByAge = sortedByAge {
if sortedByAge == "descending" {
rows = [[1, "Joe", Int32(38)], [3, "Chris", Int32(36)], [2, "Adam", Int32(28)]]
} else if sortedByAge == "ascending" {
rows = [[2, "Adam", Int32(28)], [3, "Chris", Int32(36)], [1, "Joe", Int32(38)]]
}
}
}

func fetchNext() -> [Any?]? {
Expand Down
140 changes: 140 additions & 0 deletions Tests/SwiftKueryORMTests/TestFind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import XCTest

@testable import SwiftKueryORM
import Foundation
import SwiftKuery
import KituraContracts

class TestFind: XCTestCase {
Expand All @@ -10,6 +11,10 @@ class TestFind: XCTestCase {
("testFind", testFind),
("testFindAll", testFindAll),
("testFindAllMatching", testFindAllMatching),
("testFindAllLimit", testFindAllLimit),
("testFindAllLimitAndOffset", testFindAllLimitAndOffset),
("testFindAllOrderByDescending", testFindAllOrderByDescending),
("testFindAllOrderByAscending", testFindAllOrderByAscending),
]
}

Expand Down Expand Up @@ -113,4 +118,139 @@ class TestFind: XCTestCase {
}
})
}


/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that the model can be retrieved
*/
func testFindAllLimit() {
let connection: TestConnection = createConnection(.returnOneRow)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(limit: 1) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM People LIMIT 1"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
XCTAssertEqual(array.count, 1, "Find Failed: \(String(describing: array.count)) is not equal to 1")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that the model can be retrieved
*/
func testFindAllLimitAndOffset() {
let connection: TestConnection = createConnection(.returnOneRow)
Database.default = Database(single: connection)
performTest(asyncTasks: { expectation in
Person.findAll(offset: 2, limit: 1) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM People LIMIT 1 OFFSET 2"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
XCTAssertEqual(array.count, 1, "Find Failed: \(String(describing: array.count)) is not equal to 1")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that correct amount of models are retrieved
Testing that models are sorted by age in descending order
*/
func testFindAllOrderByDescending() {
let connection: TestConnection = createConnection(.returnThreeRowsSortedDescending)
Database.default = Database(single: connection)
var column: Column?
do {
let table = try Person.getTable()
column = table.columns.first(where: {$0.name == "age"})
XCTAssertNotNil(column, "Find Failed: column not found for ordering")
guard column != nil else {
return
}
} catch let error {
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
}
performTest(asyncTasks: { expectation in
Person.findAll(orderBy: OrderBy.DESC(column!)) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM People ORDER BY People.age DESC"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
for (index, person) in array.enumerated() {
if index + 1 < array.count {
XCTAssertGreaterThanOrEqual(person.age, array[index + 1].age, "Find Failed: Age of person: \(String(describing: person.age)) is not greater than or equal to age of next person: \(String(describing: array[index + 1].age))")
}
}
XCTAssertEqual(array.count, 3, "Find Failed: \(String(describing: array.count)) is not equal to 3")
}
expectation.fulfill()
}
})
}

/**
Testing that the correct SQL Query is created to retrieve a specific model.
Testing that correct amount of models are retrieved
Testing that models are sorted by age in ascending order
*/
func testFindAllOrderByAscending() {
let connection: TestConnection = createConnection(.returnThreeRowsSortedAscending)
Database.default = Database(single: connection)
var column: Column?
do {
let table = try Person.getTable()
column = table.columns.first(where: {$0.name == "age"})
XCTAssertNotNil(column, "Find Failed: column not found for ordering")
guard column != nil else {
return
}
} catch let error {
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
}
performTest(asyncTasks: { expectation in
Person.findAll(orderBy: OrderBy.DESC(column!)) { array, error in
XCTAssertNil(error, "Find Failed: \(String(describing: error))")
XCTAssertNotNil(connection.query, "Find Failed: Query is nil")
if let query = connection.query {
let expectedQuery = "SELECT * FROM People ORDER BY People.age DESC"
let resultQuery = connection.descriptionOf(query: query)
XCTAssertEqual(resultQuery, expectedQuery, "Find Failed: Invalid query")
}
XCTAssertNotNil(array, "Find Failed: No array of models returned")
if let array = array {
for (index, person) in array.enumerated() {
if index + 1 < array.count {
XCTAssertLessThanOrEqual(person.age, array[index + 1].age, "Find Failed: Age of person: \(String(describing: person.age)) is not less than or equal to age of next person: \(String(describing: array[index + 1].age))")
}
}
XCTAssertEqual(array.count, 3, "Find Failed: \(String(describing: array.count)) is not equal to 3")
}
expectation.fulfill()
}
})
}
}

0 comments on commit 3c72ff7

Please sign in to comment.