Skip to content

Commit

Permalink
Simplify template delegate method
Browse files Browse the repository at this point in the history
  • Loading branch information
fpseverino committed Oct 20, 2024
1 parent b319928 commit 4fd2980
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 75 deletions.
4 changes: 2 additions & 2 deletions Sources/Orders/Orders.docc/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ final class OrderDelegate: OrdersDelegate {
return data
}

func template<O: OrderModel>(for order: O, db: Database) async throws -> URL {
func template<O: OrderModel>(for order: O, db: Database) async throws -> String {
// The location might vary depending on the type of order.
URL(fileURLWithPath: "Templates/Orders/", isDirectory: true)
"Templates/Orders/"
}
}
```
Expand Down
11 changes: 5 additions & 6 deletions Sources/Orders/OrdersDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import Foundation

/// The delegate which is responsible for generating the order files.
public protocol OrdersDelegate: AnyObject, Sendable {
/// Should return a `URL` which points to the template data for the order.
/// Should return a URL path which points to the template data for the order.
///
/// The URL should point to a directory containing all the images and localizations for the generated `.order` archive but should *not* contain any of these items:
/// The path should point to a directory containing all the images and localizations for the generated `.order` archive
/// but should *not* contain any of these items:
/// - `manifest.json`
/// - `order.json`
/// - `signature`
Expand All @@ -21,10 +22,8 @@ public protocol OrdersDelegate: AnyObject, Sendable {
/// - order: The order data from the SQL server.
/// - db: The SQL database to query against.
///
/// - Returns: A `URL` which points to the template data for the order.
///
/// > Important: Be sure to use the `URL(fileURLWithPath:isDirectory:)` constructor.
func template<O: OrderModel>(for order: O, db: any Database) async throws -> URL
/// - Returns: A URL path which points to the template data for the order.
func template<O: OrderModel>(for order: O, db: any Database) async throws -> String

/// Generates the SSL `signature` file.
///
Expand Down
17 changes: 5 additions & 12 deletions Sources/Orders/OrdersServiceCustom.swift
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,10 @@ extension OrdersServiceCustom {
/// - order: The order to send the notifications for.
/// - db: The `Database` to use.
public func sendPushNotifications(for order: O, on db: any Database) async throws {
try await sendPushNotificationsForOrder(
id: order.requireID(), of: order.orderTypeIdentifier, on: db)
try await sendPushNotificationsForOrder(id: order.requireID(), of: order.orderTypeIdentifier, on: db)
}

static func registrationsForOrder(
id: UUID, of orderTypeIdentifier: String, on db: any Database
) async throws -> [R] {
static func registrationsForOrder(id: UUID, of orderTypeIdentifier: String, on db: any Database) async throws -> [R] {
// This could be done by enforcing the caller to have a Siblings property wrapper,
// but there's not really any value to forcing that on them when we can just do the query ourselves like this.
try await R.query(on: db)
Expand Down Expand Up @@ -474,7 +471,7 @@ extension OrdersServiceCustom {
/// - db: The `Database` to use.
/// - Returns: The generated order content as `Data`.
public func generateOrderContent(for order: O, on db: any Database) async throws -> Data {
let templateDirectory = try await delegate.template(for: order, db: db)
let templateDirectory = try await URL(fileURLWithPath: delegate.template(for: order, db: db), isDirectory: true)
guard
(try? templateDirectory.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) ?? false
else {
Expand All @@ -490,13 +487,9 @@ extension OrdersServiceCustom {
try await self.delegate.encode(order: order, db: db, encoder: encoder)
.write(to: root.appendingPathComponent("order.json"))

try self.generateSignatureFile(
for: Self.generateManifestFile(using: encoder, in: root),
in: root
)
try self.generateSignatureFile(for: Self.generateManifestFile(using: encoder, in: root), in: root)

var files = try FileManager.default.contentsOfDirectory(
at: templateDirectory, includingPropertiesForKeys: nil)
var files = try FileManager.default.contentsOfDirectory(at: templateDirectory, includingPropertiesForKeys: nil)
files.append(URL(fileURLWithPath: "order.json", relativeTo: root))
files.append(URL(fileURLWithPath: "manifest.json", relativeTo: root))
files.append(URL(fileURLWithPath: "signature", relativeTo: root))
Expand Down
4 changes: 2 additions & 2 deletions Sources/Passes/Passes.docc/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ final class PassDelegate: PassesDelegate {
return data
}

func template<P: PassModel>(for pass: P, db: Database) async throws -> URL {
func template<P: PassModel>(for pass: P, db: Database) async throws -> String {
// The location might vary depending on the type of pass.
URL(fileURLWithPath: "Templates/Passes/", isDirectory: true)
"Templates/Passes/"
}
}
```
Expand Down
12 changes: 6 additions & 6 deletions Sources/Passes/Passes.docc/Personalization.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ A personalizable pass is just a standard pass package with the following additio
Implement the ``PassesDelegate/personalizationJSON(for:db:)`` method, which gives you the ``Pass`` to encode.
If the pass requires personalization, and if it was not already personalized, create the ``PersonalizationJSON`` struct, which will contain all the fields for the generated `personalization.json` file, and return it, otherwise return `nil`.

In the ``PassesDelegate/template(for:db:)`` method, you have to return two different directory URLs, depending on whether the pass has to be personalized or not. If it does, the directory must contain the `[email protected]` file.
In the ``PassesDelegate/template(for:db:)`` method, you have to return two different directory paths, depending on whether the pass has to be personalized or not. If it does, the directory must contain the `[email protected]` file.

Finally, you have to implement the ``PassesDelegate/encode(pass:db:encoder:)`` method as usual, but remember to use in the ``PassJSON`` initializer the user info that will be saved inside ``Pass/userPersonalization`` after the pass has been personalized.

Expand Down Expand Up @@ -69,7 +69,7 @@ final class PassDelegate: PassesDelegate {
}
}

func template<P: PassModel>(for pass: P, db: Database) async throws -> URL {
func template<P: PassModel>(for pass: P, db: Database) async throws -> String {
guard let passData = try await PassData.query(on: db)
.filter(\.$pass.$id == pass.requireID())
.first()
Expand All @@ -78,12 +78,12 @@ final class PassDelegate: PassesDelegate {
}

if passData.requiresPersonalization {
// If the pass requires personalization, return the URL to the personalization template,
// If the pass requires personalization, return the URL path to the personalization template,
// which must contain the `[email protected]` file.
return URL(fileURLWithPath: "Templates/Passes/Personalization/", isDirectory: true)
return "Templates/Passes/Personalization/"
} else {
// Otherwise, return the URL to the standard pass template.
return URL(fileURLWithPath: "Templates/Passes/Standard/", isDirectory: true)
// Otherwise, return the URL path to the standard pass template.
return "Templates/Passes/Standard/"
}
}
}
Expand Down
11 changes: 5 additions & 6 deletions Sources/Passes/PassesDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ import Foundation

/// The delegate which is responsible for generating the pass files.
public protocol PassesDelegate: AnyObject, Sendable {
/// Should return a `URL` which points to the template data for the pass.
/// Should return a URL path which points to the template data for the pass.
///
/// The URL should point to a directory containing all the images and localizations for the generated `.pkpass` archive but should *not* contain any of these items:
/// The path should point to a directory containing all the images and localizations for the generated `.pkpass` archive
/// but should *not* contain any of these items:
/// - `manifest.json`
/// - `pass.json`
/// - `personalization.json`
Expand All @@ -43,10 +44,8 @@ public protocol PassesDelegate: AnyObject, Sendable {
/// - pass: The pass data from the SQL server.
/// - db: The SQL database to query against.
///
/// - Returns: A `URL` which points to the template data for the pass.
///
/// > Important: Be sure to use the `URL(fileURLWithPath:isDirectory:)` constructor.
func template<P: PassModel>(for pass: P, db: any Database) async throws -> URL
/// - Returns: A URL path which points to the template data for the pass.
func template<P: PassModel>(for pass: P, db: any Database) async throws -> String

/// Generates the SSL `signature` file.
///
Expand Down
13 changes: 5 additions & 8 deletions Sources/Passes/PassesServiceCustom.swift
Original file line number Diff line number Diff line change
Expand Up @@ -477,13 +477,10 @@ extension PassesServiceCustom {
/// - pass: The pass to send the notifications for.
/// - db: The `Database` to use.
public func sendPushNotifications(for pass: P, on db: any Database) async throws {
try await sendPushNotificationsForPass(
id: pass.requireID(), of: pass.passTypeIdentifier, on: db)
try await sendPushNotificationsForPass(id: pass.requireID(), of: pass.passTypeIdentifier, on: db)
}

static func registrationsForPass(
id: UUID, of passTypeIdentifier: String, on db: any Database
) async throws -> [R] {
static func registrationsForPass(id: UUID, of passTypeIdentifier: String, on db: any Database) async throws -> [R] {
// This could be done by enforcing the caller to have a Siblings property wrapper,
// but there's not really any value to forcing that on them when we can just do the query ourselves like this.
try await R.query(on: db)
Expand Down Expand Up @@ -578,14 +575,12 @@ extension PassesServiceCustom {
/// - db: The `Database` to use.
/// - Returns: The generated pass content as `Data`.
public func generatePassContent(for pass: P, on db: any Database) async throws -> Data {
let templateDirectory = try await delegate.template(for: pass, db: db)
let templateDirectory = try await URL(fileURLWithPath: delegate.template(for: pass, db: db), isDirectory: true)
guard
(try? templateDirectory.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) ?? false
else {
throw PassesError.templateNotDirectory
}
var files = try FileManager.default.contentsOfDirectory(
at: templateDirectory, includingPropertiesForKeys: nil)

let tmp = FileManager.default.temporaryDirectory
let root = tmp.appendingPathComponent(UUID().uuidString, isDirectory: true)
Expand All @@ -595,6 +590,8 @@ extension PassesServiceCustom {
try await self.delegate.encode(pass: pass, db: db, encoder: self.encoder)
.write(to: root.appendingPathComponent("pass.json"))

var files = try FileManager.default.contentsOfDirectory(at: templateDirectory, includingPropertiesForKeys: nil)

// Pass Personalization
if let personalizationJSON = try await self.delegate.personalizationJSON(for: pass, db: db) {
try self.encoder.encode(personalizationJSON).write(to: root.appendingPathComponent("personalization.json"))
Expand Down
23 changes: 9 additions & 14 deletions Tests/OrdersTests/OrdersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -378,14 +378,13 @@ struct OrdersTests {
)

// Test `OrderDataMiddleware` update method
/* TODO: Fix this test
orderData.title = "Test Order 2"
do {
try await orderData.update(on: app.db)
} catch let error as HTTPClientError {
#expect(error.self == .remoteConnectionClosed)
}
*/
// TODO: Fix this test
// orderData.title = "Test Order 2"
// do {
// try await orderData.update(on: app.db)
// } catch let error as HTTPClientError {
// #expect(error.self == .remoteConnectionClosed)
// }
}
}

Expand All @@ -400,12 +399,8 @@ struct OrdersTests {
@Test("Default OrdersDelegate Properties")
func defaultDelegate() {
final class DefaultOrdersDelegate: OrdersDelegate {
func template<O: OrderModel>(for order: O, db: any Database) async throws -> URL {
URL(fileURLWithPath: "")
}
func encode<O: OrderModel>(order: O, db: any Database, encoder: JSONEncoder) async throws -> Data {
Data()
}
func template<O: OrderModel>(for order: O, db: any Database) async throws -> String { "" }
func encode<O: OrderModel>(order: O, db: any Database, encoder: JSONEncoder) async throws -> Data { Data() }
}

#expect(!DefaultOrdersDelegate().generateSignatureFile(in: URL(fileURLWithPath: "")))
Expand Down
4 changes: 2 additions & 2 deletions Tests/OrdersTests/TestOrdersDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class TestOrdersDelegate: OrdersDelegate {
return data
}

func template<O: OrderModel>(for: O, db: any Database) async throws -> URL {
URL(fileURLWithPath: "\(FileManager.default.currentDirectoryPath)/Tests/OrdersTests/Templates/", isDirectory: true)
func template<O: OrderModel>(for: O, db: any Database) async throws -> String {
"\(FileManager.default.currentDirectoryPath)/Tests/OrdersTests/Templates/"
}
}
24 changes: 9 additions & 15 deletions Tests/PassesTests/PassesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -506,14 +506,13 @@ struct PassesTests {
)

// Test `PassDataMiddleware` update method
/* TODO: Fix this test
passData.title = "Test Pass 2"
do {
try await passData.update(on: app.db)
} catch let error as HTTPClientError {
#expect(error.self == .remoteConnectionClosed)
}
*/
// TODO: Fix this test
// passData.title = "Test Pass 2"
// do {
// try await passData.update(on: app.db)
// } catch let error as HTTPClientError {
// #expect(error.self == .remoteConnectionClosed)
// }
}
}

Expand All @@ -529,13 +528,8 @@ struct PassesTests {
@Test("Default PassesDelegate Properties")
func defaultDelegate() async throws {
final class DefaultPassesDelegate: PassesDelegate {
let sslSigningFilesDirectory = URL(fileURLWithPath: "", isDirectory: true)
func template<P: PassModel>(for pass: P, db: any Database) async throws -> URL {
URL(fileURLWithPath: "")
}
func encode<P: PassModel>(pass: P, db: any Database, encoder: JSONEncoder) async throws -> Data {
Data()
}
func template<P: PassModel>(for pass: P, db: any Database) async throws -> String { "" }
func encode<P: PassModel>(pass: P, db: any Database, encoder: JSONEncoder) async throws -> Data { Data() }
}

let defaultDelegate = DefaultPassesDelegate()
Expand Down
4 changes: 2 additions & 2 deletions Tests/PassesTests/TestPassesDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class TestPassesDelegate: PassesDelegate {
}
}

func template<P: PassModel>(for pass: P, db: any Database) async throws -> URL {
URL(fileURLWithPath: "\(FileManager.default.currentDirectoryPath)/Tests/PassesTests/Templates/", isDirectory: true)
func template<P: PassModel>(for pass: P, db: any Database) async throws -> String {
"\(FileManager.default.currentDirectoryPath)/Tests/PassesTests/Templates/"
}
}

0 comments on commit 4fd2980

Please sign in to comment.