diff --git a/Sources/Orders/Orders.docc/GettingStarted.md b/Sources/Orders/Orders.docc/GettingStarted.md index 664a06c..2628281 100644 --- a/Sources/Orders/Orders.docc/GettingStarted.md +++ b/Sources/Orders/Orders.docc/GettingStarted.md @@ -136,9 +136,9 @@ final class OrderDelegate: OrdersDelegate { return data } - func template(for order: O, db: Database) async throws -> URL { + func template(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/" } } ``` diff --git a/Sources/Orders/OrdersDelegate.swift b/Sources/Orders/OrdersDelegate.swift index cdbe8d2..dc40cdc 100644 --- a/Sources/Orders/OrdersDelegate.swift +++ b/Sources/Orders/OrdersDelegate.swift @@ -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` @@ -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(for order: O, db: any Database) async throws -> URL + /// - Returns: A URL path which points to the template data for the order. + func template(for order: O, db: any Database) async throws -> String /// Generates the SSL `signature` file. /// diff --git a/Sources/Orders/OrdersServiceCustom.swift b/Sources/Orders/OrdersServiceCustom.swift index 0905e38..3e7b201 100644 --- a/Sources/Orders/OrdersServiceCustom.swift +++ b/Sources/Orders/OrdersServiceCustom.swift @@ -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) @@ -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 { @@ -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)) diff --git a/Sources/Passes/Passes.docc/GettingStarted.md b/Sources/Passes/Passes.docc/GettingStarted.md index 563645c..5f2d701 100644 --- a/Sources/Passes/Passes.docc/GettingStarted.md +++ b/Sources/Passes/Passes.docc/GettingStarted.md @@ -160,9 +160,9 @@ final class PassDelegate: PassesDelegate { return data } - func template(for pass: P, db: Database) async throws -> URL { + func template(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/" } } ``` diff --git a/Sources/Passes/Passes.docc/Personalization.md b/Sources/Passes/Passes.docc/Personalization.md index fc6743f..c828d2c 100644 --- a/Sources/Passes/Passes.docc/Personalization.md +++ b/Sources/Passes/Passes.docc/Personalization.md @@ -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 `personalizationLogo@XX.png` 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 `personalizationLogo@XX.png` 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. @@ -69,7 +69,7 @@ final class PassDelegate: PassesDelegate { } } - func template(for pass: P, db: Database) async throws -> URL { + func template(for pass: P, db: Database) async throws -> String { guard let passData = try await PassData.query(on: db) .filter(\.$pass.$id == pass.requireID()) .first() @@ -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 `personalizationLogo@XX.png` 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/" } } } diff --git a/Sources/Passes/PassesDelegate.swift b/Sources/Passes/PassesDelegate.swift index 55c2e17..ca3464b 100644 --- a/Sources/Passes/PassesDelegate.swift +++ b/Sources/Passes/PassesDelegate.swift @@ -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` @@ -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(for pass: P, db: any Database) async throws -> URL + /// - Returns: A URL path which points to the template data for the pass. + func template(for pass: P, db: any Database) async throws -> String /// Generates the SSL `signature` file. /// diff --git a/Sources/Passes/PassesServiceCustom.swift b/Sources/Passes/PassesServiceCustom.swift index 2cbe831..dd8aa02 100644 --- a/Sources/Passes/PassesServiceCustom.swift +++ b/Sources/Passes/PassesServiceCustom.swift @@ -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) @@ -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) @@ -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")) diff --git a/Tests/OrdersTests/OrdersTests.swift b/Tests/OrdersTests/OrdersTests.swift index e3e93a7..616a477 100644 --- a/Tests/OrdersTests/OrdersTests.swift +++ b/Tests/OrdersTests/OrdersTests.swift @@ -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) + // } } } @@ -400,12 +399,8 @@ struct OrdersTests { @Test("Default OrdersDelegate Properties") func defaultDelegate() { final class DefaultOrdersDelegate: OrdersDelegate { - func template(for order: O, db: any Database) async throws -> URL { - URL(fileURLWithPath: "") - } - func encode(order: O, db: any Database, encoder: JSONEncoder) async throws -> Data { - Data() - } + func template(for order: O, db: any Database) async throws -> String { "" } + func encode(order: O, db: any Database, encoder: JSONEncoder) async throws -> Data { Data() } } #expect(!DefaultOrdersDelegate().generateSignatureFile(in: URL(fileURLWithPath: ""))) diff --git a/Tests/OrdersTests/TestOrdersDelegate.swift b/Tests/OrdersTests/TestOrdersDelegate.swift index 7fa2485..b5b45bb 100644 --- a/Tests/OrdersTests/TestOrdersDelegate.swift +++ b/Tests/OrdersTests/TestOrdersDelegate.swift @@ -18,7 +18,7 @@ final class TestOrdersDelegate: OrdersDelegate { return data } - func template(for: O, db: any Database) async throws -> URL { - URL(fileURLWithPath: "\(FileManager.default.currentDirectoryPath)/Tests/OrdersTests/Templates/", isDirectory: true) + func template(for: O, db: any Database) async throws -> String { + "\(FileManager.default.currentDirectoryPath)/Tests/OrdersTests/Templates/" } } diff --git a/Tests/PassesTests/PassesTests.swift b/Tests/PassesTests/PassesTests.swift index 65689e0..c0d1147 100644 --- a/Tests/PassesTests/PassesTests.swift +++ b/Tests/PassesTests/PassesTests.swift @@ -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) + // } } } @@ -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(for pass: P, db: any Database) async throws -> URL { - URL(fileURLWithPath: "") - } - func encode(pass: P, db: any Database, encoder: JSONEncoder) async throws -> Data { - Data() - } + func template(for pass: P, db: any Database) async throws -> String { "" } + func encode(pass: P, db: any Database, encoder: JSONEncoder) async throws -> Data { Data() } } let defaultDelegate = DefaultPassesDelegate() diff --git a/Tests/PassesTests/TestPassesDelegate.swift b/Tests/PassesTests/TestPassesDelegate.swift index 2d556bf..c323c8d 100644 --- a/Tests/PassesTests/TestPassesDelegate.swift +++ b/Tests/PassesTests/TestPassesDelegate.swift @@ -40,7 +40,7 @@ final class TestPassesDelegate: PassesDelegate { } } - func template(for pass: P, db: any Database) async throws -> URL { - URL(fileURLWithPath: "\(FileManager.default.currentDirectoryPath)/Tests/PassesTests/Templates/", isDirectory: true) + func template(for pass: P, db: any Database) async throws -> String { + "\(FileManager.default.currentDirectoryPath)/Tests/PassesTests/Templates/" } }