Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get HummingbirdFluent working with HB 2.0 #11

Merged
merged 14 commits into from
Jan 5, 2024
2 changes: 1 addition & 1 deletion .github/workflows/api-breakage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
linux:
runs-on: ubuntu-latest
container:
image: swift:5.8
image: swift:5.9
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
12 changes: 5 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ on:
push:
branches:
- main
- 2.x.x
paths:
- '**.swift'
- '**.yml'
pull_request:
branches:
- main
paths:
- '**.swift'
- '**.yml'
Expand All @@ -21,15 +20,14 @@ jobs:
strategy:
matrix:
image:
- 'swift:5.6'
- 'swift:5.7'
- 'swift:5.8'
- 'swift:5.9'
- 'swiftlang/swift:nightly-5.10-jammy'

container:
image: ${{ matrix.image }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build
run: |
swift build
swift test
14 changes: 8 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
// swift-tools-version:5.6
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "hummingbird-fluent",
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13)],
platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)],
products: [
.library(name: "HummingbirdFluent", targets: ["HummingbirdFluent"]),
],
dependencies: [
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-async-algorithms.git", from: "1.0.0"),
.package(url: "https://github.com/hummingbird-project/hummingbird.git", branch: "2.x.x"),
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.17.0"),
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.0.0"),
// used in tests
.package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.0.0"),
// .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"),
// .package(url: "https://github.com/vapor/fluent-mysql-driver.git", from: "4.0.0"),
],
targets: [
.target(name: "HummingbirdFluent", dependencies: [
.product(name: "Hummingbird", package: "hummingbird"),
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
.product(name: "FluentKit", package: "fluent-kit"),
.product(name: "Hummingbird", package: "hummingbird"),
.product(name: "ServiceLifecycle", package: "swift-service-lifecycle"),
]),
.testTarget(name: "HummingbirdFluentTests", dependencies: [
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
// .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
// .product(name: "FluentMySQLDriver", package: "fluent-mysql-driver"),
.byName(name: "HummingbirdFluent"),
.product(name: "HummingbirdFoundation", package: "hummingbird"),
.product(name: "HummingbirdXCT", package: "hummingbird"),
Expand Down
47 changes: 0 additions & 47 deletions Sources/HummingbirdFluent/Application+fluent.swift

This file was deleted.

122 changes: 41 additions & 81 deletions Sources/HummingbirdFluent/Fluent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,85 +14,57 @@

import FluentKit
import Hummingbird
import ServiceLifecycle

@MainActor
public struct MainActorBox<Value>: Sendable {
public let value: Value
}

extension DatabaseID: @unchecked Sendable {}

/// Manage fluent databases and migrations
///
/// You can either create this separate from `HBApplication` or add it to your application
/// using `HBApplication.addFluent`.
public struct HBFluent {
/// Fluent history management
public class History {
/// Is history recording enabled
public private(set) var enabled: Bool
// History of queries to Fluent
public private(set) var history: QueryHistory?

init() {
self.enabled = false
self.history = nil
}

/// Start recording history
public func start() {
self.enabled = true
self.history = .init()
}

/// Stop recording history
public func stop() {
self.enabled = false
}

/// Clear history
public func clear() {
self.history = .init()
}
}

/// Databases attached
public let databases: Databases
/// List of migrations
public let migrations: Migrations
/// Event loop group used by migrator
public struct HBFluent: Sendable, Service {
/// Event loop group
public let eventLoopGroup: EventLoopGroup
/// Logger
public let logger: Logger
/// Fluent history setup
public let history: History
/// List of migrations. Only accessible from the main actor
@MainActor
public var migrations: Migrations { self._migrations.value }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently using @MainActor here. @Joannis what are your thoughts on setting up a Hummingbird global actor in Hummingbird and using that instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you foresee other places where you'd want/need this? What's the "domain" of the actor? What type of information do you scope it to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point I time probably not so we can keep as @MainActor. The migration stuff should only be setup during initialisation.

/// Databases attached
public var databases: Databases { self._databases.wrappedValue }

/// Initialize HBFluent
/// - Parameter application: application to get NIOThreadPool, EventLoopGroup and Logger from
init(application: HBApplication) {
self.databases = Databases(threadPool: application.threadPool, on: application.eventLoopGroup)
self.migrations = .init()
self.eventLoopGroup = application.eventLoopGroup
self.logger = application.logger
self.history = .init()
}
private let _databases: UnsafeTransfer<Databases>
private let _migrations: MainActorBox<Migrations>

/// Initialize HBFluent
/// - Parameters:
/// - eventLoopGroup: EventLoopGroup used by databases
/// - threadPool: NIOThreadPool used by databases
/// - logger: Logger used by databases
public init(
eventLoopGroup: EventLoopGroup,
threadPool: NIOThreadPool,
eventLoopGroupProvider: EventLoopGroupProvider = .singleton,
threadPool: NIOThreadPool = .singleton,
logger: Logger
) {
self.databases = Databases(threadPool: threadPool, on: eventLoopGroup)
self.migrations = .init()
let eventLoopGroup = eventLoopGroupProvider.eventLoopGroup
self._databases = .init(Databases(threadPool: threadPool, on: eventLoopGroup))
self._migrations = .init(value: .init())
self.eventLoopGroup = eventLoopGroup
self.logger = logger
self.history = .init()
}

/// Shutdown databases
public func shutdown() {
self.databases.shutdown()
public func run() async throws {
await GracefulShutdownWaiter().wait()
self._databases.wrappedValue.shutdown()
}

/// fluent migrator
@MainActor
public var migrator: Migrator {
Migrator(
databases: self.databases,
Expand All @@ -103,46 +75,34 @@ public struct HBFluent {
}

/// Run migration if needed
public func migrate() -> EventLoopFuture<Void> {
self.migrator.setupIfNeeded().flatMap {
self.migrator.prepareBatch()
}
@MainActor
public func migrate() async throws {
try await self.migrator.setupIfNeeded().get()
try await self.migrator.prepareBatch().get()
}

/// Run revert if needed
public func revert() -> EventLoopFuture<Void> {
self.migrator.setupIfNeeded().flatMap {
self.migrator.revertAllBatches()
}
@MainActor
public func revert() async throws {
try await self.migrator.setupIfNeeded().get()
try await self.migrator.revertAllBatches().get()
}

/// Return Database connection
///
/// - Parameters:
/// - id: ID of database
/// - eventLoop: Eventloop database connection is running on
/// - history: Query history storage
/// - pageSizeLimit: Set page size limit to avoid server overload
/// - Returns: Database connection
public func db(_ id: DatabaseID? = nil, on eventLoop: EventLoop) -> Database {
self.databases
public func db(_ id: DatabaseID? = nil, history: QueryHistory? = nil, pageSizeLimit: Int? = nil) -> Database {
self._databases.wrappedValue
.database(
id,
logger: self.logger,
on: eventLoop,
history: self.history.enabled ? self.history.history : nil
on: self.eventLoopGroup.any(),
history: history,
pageSizeLimit: pageSizeLimit
)!
}
}

/// async/await
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
extension HBFluent {
/// Run migration if needed
public func migrate() async throws {
try await self.migrate().get()
}

/// Run revert if needed
public func revert() async throws {
try await self.revert().get()
}
}
Loading
Loading