Skip to content

Commit

Permalink
FluentKit 1.0.0 GM (#178)
Browse files Browse the repository at this point in the history
* fluent gm

* rm nested

* version update

* fix ci

* fix ci
  • Loading branch information
tanner0101 authored May 29, 2020
1 parent 1eee16d commit 60ce51c
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 80 deletions.
84 changes: 53 additions & 31 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,52 +1,70 @@
name: Test Matrix
on: ['pull_request']
on:
- pull_request
defaults:
run:
shell: bash
jobs:
PR-tests-linux:
linux:
strategy:
fail-fast: false
matrix:
dbimage: ['mysql:5.7', 'mysql:8.0', 'mariadb:latest']
runner: [
'swift:5.2-xenial', 'swift:5.2-bionic',
'swiftlang/swift:nightly-5.2-xenial', 'swiftlang/swift:nightly-5.2-bionic',
'swiftlang/swift:nightly-5.3-xenial', 'swiftlang/swift:nightly-5.3-bionic',
'swiftlang/swift:nightly-master-xenial', 'swiftlang/swift:nightly-master-bionic',
'swiftlang/swift:nightly-master-focal',
'swiftlang/swift:nightly-master-centos8',
'swiftlang/swift:nightly-master-amazonlinux2'
]
dbimage:
- mysql:5.7
- mysql:8.0
- mariadb:latest
runner:
# 5.2 Stable
- swift:5.2-xenial
- swift:5.2-bionic
# 5.2 Unstable
- swiftlang/swift:nightly-5.2-xenial
- swiftlang/swift:nightly-5.2-bionic
# 5.3 Unstable
- swiftlang/swift:nightly-5.3-xenial
- swiftlang/swift:nightly-5.3-bionic
# Master Unsable
- swiftlang/swift:nightly-master-xenial
- swiftlang/swift:nightly-master-bionic
- swiftlang/swift:nightly-master-focal
- swiftlang/swift:nightly-master-centos8
- swiftlang/swift:nightly-master-amazonlinux2
include:
- installcmd: 'apt-get -q update && apt-get -q install -y mysql-client'
- { 'runner': 'swiftlang/swift:nightly-master-centos8', 'installcmd': 'dnf install -y zlib-devel mysql' }
- { 'runner': 'swiftlang/swift:nightly-master-amazonlinux2', 'installcmd': 'yum install -y zlib-devel mysql' }
- runner: swiftlang/swift:nightly-master-centos8
installcmd: dnf install -y zlib-devel
- runner: swiftlang/swift:nightly-master-amazonlinux2
installcmd: yum install -y zlib-devel
exclude:
- { 'runner': 'swiftlang/swift:nightly-master-amazonlinux2', 'dbimage': 'mysql:8.0' }
- runner: swiftlang/swift:nightly-master-amazonlinux2
dbimage: mysql:8.0
container: ${{ matrix.runner }}
runs-on: ubuntu-latest
services:
mysql:
mysql-a:
image: ${{ matrix.dbimage }}
env: { MYSQL_ALLOW_EMPTY_PASSWORD: true, MYSQL_USER: vapor_username, MYSQL_PASSWORD: vapor_password }
env:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
MYSQL_USER: vapor_username
MYSQL_PASSWORD: vapor_password
MYSQL_DATABASE: vapor_database
mysql-b:
image: ${{ matrix.dbimage }}
env:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
MYSQL_USER: vapor_username
MYSQL_PASSWORD: vapor_password
MYSQL_DATABASE: vapor_database
steps:
- name: Install dependencies
run: ${{ matrix.installcmd }}
- name: Preconfigure MySQL client
run: printf '[client]\nhost=mysql\n\n[mysql]\nbatch\n' >>/etc/my.cnf
- name: Wait for MySQL server to be ready
run: until echo | mysql; do sleep 1; done
timeout-minutes: 5
- name: Set up MySQL databases and privileges
run: |
for db in vapor_{database,migration_extra}; do mysql <<<"CREATE DATABASE $db; GRANT ALL PRIVILEGES ON $db.* TO vapor_username@'%';"; done
- name: Check out code
uses: actions/checkout@v2
- name: Run tests with Thread Sanitizer
run: swift test --enable-test-discovery --sanitize=thread
env: { 'MYSQL_HOSTNAME': 'mysql' }
PR-tests-macos:
env:
MYSQL_HOSTNAME_A: mysql-a
MYSQL_HOSTNAME_B: mysql-b
macOS:
strategy:
fail-fast: false
matrix:
Expand All @@ -70,11 +88,15 @@ jobs:
run: |
mysql -u${{ matrix.username }} --batch <<-'SQL'
CREATE USER vapor_username@localhost IDENTIFIED BY 'vapor_password';
CREATE DATABASE vapor_database; GRANT ALL PRIVILEGES ON vapor_database.* TO vapor_username@localhost;
CREATE DATABASE vapor_migration_extra; GRANT ALL PRIVILEGES ON vapor_migration_extra.* TO vapor_username@localhost;
CREATE DATABASE vapor_database_a; GRANT ALL PRIVILEGES ON vapor_database_a.* TO vapor_username@localhost;
CREATE DATABASE vapor_database_b; GRANT ALL PRIVILEGES ON vapor_database_b.* TO vapor_username@localhost;
SQL
- name: Check out code
uses: actions/checkout@v2
- name: Run tests with Thread Sanitizer
run: swift test --enable-test-discovery --sanitize=thread
env: { 'MYSQL_HOSTNAME': '127.0.0.1' }
env:
MYSQL_HOSTNAME_A: '127.0.0.1'
MYSQL_HOSTNAME_B: '127.0.0.1'
MYSQL_DATABASE_A: vapor_database_a
MYSQL_DATABASE_B: vapor_database_b
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let package = Package(
.library(name: "FluentMySQLDriver", targets: ["FluentMySQLDriver"]),
],
dependencies: [
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.0.0-rc.1"),
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.0.0-rc.2"),
.package(url: "https://github.com/vapor/mysql-kit.git", from: "4.0.0-rc.1.2"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
],
Expand Down
24 changes: 16 additions & 8 deletions Sources/FluentMySQLDriver/FluentMySQLDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AsyncKit
struct _FluentMySQLDatabase {
let database: MySQLDatabase
let context: DatabaseContext
let inTransaction: Bool
}

extension _FluentMySQLDatabase: Database {
Expand Down Expand Up @@ -56,9 +57,12 @@ extension _FluentMySQLDatabase: Database {
}

func transaction<T>(_ closure: @escaping (Database) -> EventLoopFuture<T>) -> EventLoopFuture<T> {
self.database.withConnection { conn in
guard !self.inTransaction else {
return closure(self)
}
return self.database.withConnection { conn in
conn.simpleQuery("START TRANSACTION").flatMap { _ in
let db = _FluentMySQLDatabase(database: conn, context: self.context)
let db = _FluentMySQLDatabase(database: conn, context: self.context, inTransaction: true)
return closure(db).flatMap { result in
conn.simpleQuery("COMMIT").map { _ in
result
Expand All @@ -74,7 +78,7 @@ extension _FluentMySQLDatabase: Database {

func withConnection<T>(_ closure: @escaping (Database) -> EventLoopFuture<T>) -> EventLoopFuture<T> {
self.database.withConnection {
closure(_FluentMySQLDatabase(database: $0, context: self.context))
closure(_FluentMySQLDatabase(database: $0, context: self.context, inTransaction: self.inTransaction))
}
}
}
Expand Down Expand Up @@ -114,15 +118,19 @@ private struct LastInsertRow: DatabaseOutput {
self
}

func contains(_ path: [FieldKey]) -> Bool {
path[0] == .id || path[0] == self.customIDKey
func decodeNil(_ key: FieldKey) throws -> Bool {
false
}

func contains(_ key: FieldKey) -> Bool {
key == .id || key == self.customIDKey
}

func decode<T>(_ path: [FieldKey], as type: T.Type) throws -> T
func decode<T>(_ key: FieldKey, as type: T.Type) throws -> T
where T: Decodable
{
guard self.contains(path) else {
fatalError("Cannot decode field from last insert row: \(path).")
guard self.contains(key) else {
fatalError("Cannot decode field from last insert row: \(key).")
}
if let lastInsertIDInitializable = T.self as? LastInsertIDInitializable.Type {
return lastInsertIDInitializable.init(lastInsertID: self.metadata.lastInsertID!) as! T
Expand Down
3 changes: 2 additions & 1 deletion Sources/FluentMySQLDriver/FluentMySQLDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ struct _FluentMySQLDriver: DatabaseDriver {
func makeDatabase(with context: DatabaseContext) -> Database {
_FluentMySQLDatabase(
database: self.pool.pool(for: context.eventLoop).database(logger: context.logger),
context: context
context: context,
inTransaction: false
)
}

Expand Down
5 changes: 5 additions & 0 deletions Sources/FluentMySQLDriver/MySQLConverterDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import FluentSQL

struct MySQLConverterDelegate: SQLConverterDelegate {
func nestedFieldExpression(_ column: String, _ path: [String]) -> SQLExpression {
let path = path.joined(separator: ".")
return SQLRaw("JSON_EXTRACT(\(column), '$.\(path)')")
}

func customDataType(_ dataType: DatabaseSchema.DataType) -> SQLExpression? {
switch dataType {
case .string: return SQLRaw("VARCHAR(255)")
Expand Down
29 changes: 17 additions & 12 deletions Sources/FluentMySQLDriver/MySQLRow+Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ private struct _MySQLDatabaseOutput: DatabaseOutput {
self.row.description
}

func contains(_ path: [FieldKey]) -> Bool {
return self.row.column(self.columnName(path)) != nil
func contains(_ key: FieldKey) -> Bool {
self.row.column(self.columnName(key)) != nil
}

func decodeNil(_ key: FieldKey) throws -> Bool {
if let data = self.row.column((self.columnName(key))) {
return data.buffer == nil
} else {
return true
}
}

func schema(_ schema: String) -> DatabaseOutput {
Expand All @@ -23,21 +31,18 @@ private struct _MySQLDatabaseOutput: DatabaseOutput {
)
}

func decode<T>(
_ path: [FieldKey],
as type: T.Type
) throws -> T where T : Decodable {
try self.row.decode(column: self.columnName(path), as: T.self)
func decode<T>(_ key: FieldKey, as type: T.Type) throws -> T
where T: Decodable
{
try self.row.decode(column: self.columnName(key), as: T.self)
}


private func columnName(_ path: [FieldKey]) -> String {
let field = path.map { $0.description }.joined(separator: "_")
private func columnName(_ key: FieldKey) -> String {
if let schema = self.schema {
return "\(schema)_\(field)"
return "\(schema)_\(key.description)"
} else {
return field
return key.description
}

}
}
50 changes: 24 additions & 26 deletions Tests/FluentMySQLDriverTests/FluentMySQLDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ final class FluentMySQLDriverTests: XCTestCase {
func testArray() throws { try self.benchmarker.testArray() }
func testBatch() throws { try self.benchmarker.testBatch() }
func testChildren() throws { try self.benchmarker.testChildren() }
func testCodable() throws { try self.benchmarker.testCodable() }
func testChunk() throws { try self.benchmarker.testChunk() }
func testCRUD() throws { try self.benchmarker.testCRUD() }
func testEagerLoad() throws { try self.benchmarker.testEagerLoad() }
Expand All @@ -26,6 +27,7 @@ final class FluentMySQLDriverTests: XCTestCase {
func testParent() throws { try self.benchmarker.testParent() }
func testPerformance() throws { try self.benchmarker.testPerformance() }
func testRange() throws { try self.benchmarker.testRange() }
func testSchema() throws { try self.benchmarker.testSchema() }
func testSet() throws { try self.benchmarker.testSet() }
func testSiblings() throws { try self.benchmarker.testSiblings() }
func testSoftDelete() throws { try self.benchmarker.testSoftDelete() }
Expand Down Expand Up @@ -287,40 +289,35 @@ final class FluentMySQLDriverTests: XCTestCase {
self.threadPool = NIOThreadPool(numberOfThreads: 1)
self.dbs = Databases(threadPool: threadPool, on: self.eventLoopGroup)

let databaseA = env("MYSQL_DATABASE_A") ?? "vapor_database"
let databaseB = env("MYSQL_DATABASE_B") ?? "vapor_database"
self.dbs.use(.mysql(
hostname: env("MYSQL_HOSTNAME") ?? "localhost",
port: env("MYSQL_PORT").flatMap(Int.init) ?? 3306,
hostname: env("MYSQL_HOSTNAME_A") ?? "localhost",
port: env("MYSQL_PORT_A").flatMap(Int.init) ?? 3306,
username: "vapor_username",
password: "vapor_password",
database: "vapor_database",
database: databaseA,
tlsConfiguration: .forClient(certificateVerification: .none)
), as: .mysql)
), as: .a)

self.dbs.use(.mysql(
hostname: env("MYSQL_HOSTNAME") ?? "localhost",
port: env("MYSQL_PORT").flatMap(Int.init) ?? 3306,
hostname: env("MYSQL_HOSTNAME_B") ?? "localhost",
port: env("MYSQL_PORT_B").flatMap(Int.init) ?? 3306,
username: "vapor_username",
password: "vapor_password",
database: "vapor_migration_extra",
database: databaseB,
tlsConfiguration: .forClient(certificateVerification: .none)
), as: .migrationExtra)

// clear db.
let databaseExtra = try XCTUnwrap(
self.benchmarker.databases.database(
.migrationExtra,
logger: Logger(label: "test.fluent.migration_extra"),
on: self.eventLoopGroup.next()
) as? MySQLDatabase
)

_ = try self.mysql.simpleQuery("DROP DATABASE vapor_database").wait()
_ = try self.mysql.simpleQuery("CREATE DATABASE vapor_database").wait()
_ = try self.mysql.simpleQuery("USE vapor_database").wait()

_ = try databaseExtra.simpleQuery("DROP DATABASE vapor_migration_extra").wait()
_ = try databaseExtra.simpleQuery("CREATE DATABASE vapor_migration_extra").wait()
_ = try databaseExtra.simpleQuery("USE vapor_migration_extra").wait()
), as: .b)

// clear dbs
let a = self.dbs.database(.a, logger: Logger(label: "test.fluent.a"), on: self.eventLoopGroup.next())! as! MySQLDatabase
_ = try a.simpleQuery("DROP DATABASE \(databaseA)").wait()
_ = try a.simpleQuery("CREATE DATABASE \(databaseA)").wait()
_ = try a.simpleQuery("USE \(databaseA)").wait()
let b = self.dbs.database(.b, logger: Logger(label: "test.fluent.b"), on: self.eventLoopGroup.next())! as! MySQLDatabase
_ = try b.simpleQuery("DROP DATABASE \(databaseB)").wait()
_ = try b.simpleQuery("CREATE DATABASE \(databaseB)").wait()
_ = try b.simpleQuery("USE \(databaseB)").wait()
}

override func tearDownWithError() throws {
Expand All @@ -333,7 +330,8 @@ final class FluentMySQLDriverTests: XCTestCase {
}

extension DatabaseID {
static let migrationExtra = DatabaseID(string: "migration_extra")
static let a = DatabaseID(string: "mysql_a")
static let b = DatabaseID(string: "mysql_b")
}

func env(_ name: String) -> String? {
Expand Down
11 changes: 10 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3'

services:
mysql-8:
mysql-8-a:
image: mysql:8.0
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
Expand All @@ -10,6 +10,15 @@ services:
MYSQL_PASSWORD: vapor_password
ports:
- 3306:3306
mysql-8-b:
image: mysql:8.0
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
MYSQL_DATABASE: vapor_database
MYSQL_USER: vapor_username
MYSQL_PASSWORD: vapor_password
ports:
- 3307:3306
mysql:
image: mysql:5.7
environment:
Expand Down

0 comments on commit 60ce51c

Please sign in to comment.