From 7c11bd334017ec38c5578c7a4b5a3f7c3df385ff Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 4 Aug 2024 09:21:27 -0700 Subject: [PATCH] feat: Add ability to pass routes into configuration (#74) * feat: Add ability to pass routes into configuration * lint --- Package.resolved | 20 ++--- Package.swift | 20 +++-- README.md | 5 +- Sources/App/entrypoint.swift | 5 +- Sources/ParseServerSwift/Parse.swift | 43 +++++++---- Sources/ParseServerSwift/configure.swift | 3 +- Sources/ParseServerSwift/routes.swift | 87 +++++++++++++++------- Tests/ParseServerSwiftTests/AppTests.swift | 71 +++++++++++------- 8 files changed, 163 insertions(+), 91 deletions(-) diff --git a/Package.resolved b/Package.resolved index 61a168f3..076a42c4 100644 --- a/Package.resolved +++ b/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/async-kit.git", "state" : { - "revision" : "7ece208cd401687641c88367a00e3ea2b04311f1", - "version" : "1.19.0" + "revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31", + "version" : "1.20.0" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/netreconlab/Parse-Swift.git", "state" : { - "revision" : "48b38b15363846e0714bc632c1939f721f71f4b2", - "version" : "5.11.1" + "revision" : "b56de0a0770fb3ac267d3d8d1cc3924fbfbf3d16", + "version" : "5.11.2" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "fc79798d5a150d61361a27ce0c51169b889e23de", - "version" : "2.68.0" + "revision" : "e4abde8be0e49dc7d66e6eed651254accdcd9533", + "version" : "2.69.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "a0224f3d20438635dd59c9fcc593520d80d131d0", - "version" : "1.33.0" + "revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94", + "version" : "1.34.0" } }, { @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "6a9e38e7bd22a3b8ba80bddf395623cf68f57807", - "version" : "1.3.1" + "revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5", + "version" : "1.3.2" } }, { diff --git a/Package.swift b/Package.swift index 70ed7406..e1f6ba2f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.6 +// swift-tools-version:5.7 import PackageDescription // swiftlint:disable line_length @@ -21,11 +21,11 @@ let package = Package( dependencies: [ .package( url: "https://github.com/vapor/vapor.git", - .upToNextMajor(from: "4.102.1") + .upToNextMajor(from: "4.102.1") ), .package( url: "https://github.com/netreconlab/Parse-Swift.git", - .upToNextMajor(from: "5.11.1") + .upToNextMajor(from: "5.11.2") ) ], targets: [ @@ -43,10 +43,14 @@ let package = Package( // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release // builds. See for details. .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)) - ]), - .testTarget(name: "ParseServerSwiftTests", dependencies: [ - .target(name: "ParseServerSwift"), - .product(name: "XCTVapor", package: "vapor") - ]) + ] + ), + .testTarget( + name: "ParseServerSwiftTests", + dependencies: [ + .target(name: "ParseServerSwift"), + .product(name: "XCTVapor", package: "vapor") + ] + ) ] ) diff --git a/README.md b/README.md index 265a183d..717910ee 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,10 @@ enum Entrypoint { let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor() app.logger.debug("Running with \(executorTakeoverSuccess ? "SwiftNIO" : "standard") Swift Concurrency default executor") - try await parseServerSwiftConfigure(app) + try await parseServerSwiftConfigure( + app, + using: exampleRoutes + ) try await app.execute() try await app.asyncShutdown() } diff --git a/Sources/App/entrypoint.swift b/Sources/App/entrypoint.swift index 33309570..c4436d31 100644 --- a/Sources/App/entrypoint.swift +++ b/Sources/App/entrypoint.swift @@ -28,7 +28,10 @@ enum Entrypoint { "Running with \(executorTakeoverSuccess ? "SwiftNIO" : "standard") Swift Concurrency default executor" ) - try await parseServerSwiftConfigure(app) + try await parseServerSwiftConfigure( + app, + using: exampleRoutes + ) try await app.execute() try await app.asyncShutdown() } diff --git a/Sources/ParseServerSwift/Parse.swift b/Sources/ParseServerSwift/Parse.swift index 7ef219dc..d7aac18e 100644 --- a/Sources/ParseServerSwift/Parse.swift +++ b/Sources/ParseServerSwift/Parse.swift @@ -30,42 +30,53 @@ public var configuration: ParseServerConfiguration { server URL. - warning: Be sure to call this method before calling `try routes(app)`. */ -public func initialize(_ configuration: ParseServerConfiguration, - app: Application) async throws { +public func initialize( + _ configuration: ParseServerConfiguration, + app: Application +) async throws { try await initializeServer(configuration, app: app) } -func initialize(_ configuration: ParseServerConfiguration, - app: Application, - testing: Bool) async throws { +func initialize( + _ configuration: ParseServerConfiguration, + app: Application, + testing: Bool +) async throws { var configuration = configuration configuration.isTesting = testing try await initialize(configuration, app: app) } -func initializeServer(_ configuration: ParseServerConfiguration, - app: Application) async throws { +func initializeServer( + _ configuration: ParseServerConfiguration, + app: Application +) async throws { // Parse uses tailored encoders/decoders. These can be retrieved from any ParseObject ContentConfiguration.global.use(encoder: User.getEncoder(), for: .json) ContentConfiguration.global.use(decoder: User.getDecoder(), for: .json) guard let parseServerURL = URL(string: configuration.primaryParseServerURLString) else { - throw ParseError(code: .otherCause, - message: "Could not make a URL from the Parse Server string") + let error = ParseError( + code: .otherCause, + message: "Could not make a URL from the Parse Server string" + ) + throw error } if !configuration.isTesting { try setConfiguration(configuration) do { // Initialize the Parse-Swift SDK. Add any additional parameters you need - try await ParseSwift.initialize(applicationId: configuration.applicationId, - primaryKey: configuration.primaryKey, - serverURL: parseServerURL, - // POST all queries instead of using GET. - usingPostForQuery: true, - // Do not use cache for anything. - requestCachePolicy: .reloadIgnoringLocalCacheData) { _, completionHandler in + try await ParseSwift.initialize( + applicationId: configuration.applicationId, + primaryKey: configuration.primaryKey, + serverURL: parseServerURL, + // POST all queries instead of using GET. + usingPostForQuery: true, + // Do not use cache for anything. + requestCachePolicy: .reloadIgnoringLocalCacheData + ) { _, completionHandler in // Setup to use default certificate pinning. See Parse-Swift docs for more info completionHandler(.performDefaultHandling, nil) } diff --git a/Sources/ParseServerSwift/configure.swift b/Sources/ParseServerSwift/configure.swift index f536a3c4..c1595573 100644 --- a/Sources/ParseServerSwift/configure.swift +++ b/Sources/ParseServerSwift/configure.swift @@ -9,7 +9,8 @@ import Vapor */ public func parseServerSwiftConfigure( _ app: Application, - with configuration: ParseServerConfiguration? = nil + with configuration: ParseServerConfiguration? = nil, + using routes: ((Application) throws -> Void) ) async throws { // Initialize ParseServerSwift let configuration = try configuration ?? ParseServerConfiguration(app: app) diff --git a/Sources/ParseServerSwift/routes.swift b/Sources/ParseServerSwift/routes.swift index f3aa047a..97e57f5b 100644 --- a/Sources/ParseServerSwift/routes.swift +++ b/Sources/ParseServerSwift/routes.swift @@ -2,7 +2,7 @@ import Vapor import ParseSwift // swiftlint:disable:next cyclomatic_complexity function_body_length -func routes(_ app: Application) throws { +public func exampleRoutes(_ app: Application) throws { // A typical route in Vapor. app.get { req in @@ -15,8 +15,10 @@ func routes(_ app: Application) throws { } // A simple Parse Hook Function route that returns "Hello World". - app.post("hello", - name: "hello") { req async throws -> ParseHookResponse in + app.post( + "hello", + name: "hello" + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "String" type. if let error: ParseHookResponse = checkHeaders(req) { @@ -39,8 +41,10 @@ func routes(_ app: Application) throws { } // Another simple Parse Hook Function route that returns the version of the server. - app.post("version", - name: "version") { req async throws -> ParseHookResponse in + app.post( + "version", + name: "version" + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "String" type. if let error: ParseHookResponse = checkHeaders(req) { @@ -89,9 +93,13 @@ func routes(_ app: Application) throws { } // A Parse Hook Trigger route. - app.post("score", "save", "before", - object: GameScore.self, - trigger: .beforeSave) { req async throws -> ParseHookResponse in + app.post( + "score", + "save", + "before", + object: GameScore.self, + trigger: .beforeSave + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "GameScore" type. if let error: ParseHookResponse = checkHeaders(req) { @@ -117,9 +125,13 @@ func routes(_ app: Application) throws { } // Another Parse Hook Trigger route. - app.post("score", "find", "before", - object: GameScore.self, - trigger: .beforeFind) { req async throws -> ParseHookResponse<[GameScore]> in + app.post( + "score", + "find", + "before", + object: GameScore.self, + trigger: .beforeFind + ) { req async throws -> ParseHookResponse<[GameScore]> in // Note that `ParseHookResponse<[GameScore]>` means a "successful" // response will return a "[GameScore]" type. if let error: ParseHookResponse<[GameScore]> = checkHeaders(req) { @@ -144,9 +156,13 @@ func routes(_ app: Application) throws { } // Another Parse Hook Trigger route. - app.post("user", "login", "after", - object: User.self, - trigger: .afterLogin) { req async throws -> ParseHookResponse in + app.post( + "user", + "login", + "after", + object: User.self, + trigger: .afterLogin + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. @@ -161,8 +177,12 @@ func routes(_ app: Application) throws { } // A Parse Hook Trigger route for `ParseFile`. - app.on("file", "save", "before", - trigger: .beforeSave) { req async throws -> ParseHookResponse in + app.on( + "file", + "save", + "before", + trigger: .beforeSave + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. Sending "false" @@ -178,8 +198,12 @@ func routes(_ app: Application) throws { } // Another Parse Hook Trigger route for `ParseFile`. - app.post("file", "delete", "before", - trigger: .beforeDelete) { req async throws -> ParseHookResponse in + app.post( + "file", + "delete", + "before", + trigger: .beforeDelete + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. @@ -194,8 +218,11 @@ func routes(_ app: Application) throws { } // A Parse Hook Trigger route for `ParseLiveQuery`. - app.post("connect", "before", - trigger: .beforeConnect) { req async throws -> ParseHookResponse in + app.post( + "connect", + "before", + trigger: .beforeConnect + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. @@ -210,9 +237,13 @@ func routes(_ app: Application) throws { } // Another Parse Hook Trigger route for `ParseLiveQuery`. - app.post("score", "subscribe", "before", - object: GameScore.self, - trigger: .beforeSubscribe) { req async throws -> ParseHookResponse in + app.post( + "score", + "subscribe", + "before", + object: GameScore.self, + trigger: .beforeSubscribe + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. @@ -227,9 +258,13 @@ func routes(_ app: Application) throws { } // Another Parse Hook Trigger route for `ParseLiveQuery`. - app.post("score", "event", "after", - object: GameScore.self, - trigger: .afterEvent) { req async throws -> ParseHookResponse in + app.post( + "score", + "event", + "after", + object: GameScore.self, + trigger: .afterEvent + ) { req async throws -> ParseHookResponse in // Note that `ParseHookResponse` means a "successful" // response will return a "Bool" type. Bool is the standard response with // a "true" response meaning everything is okay or continue. diff --git a/Tests/ParseServerSwiftTests/AppTests.swift b/Tests/ParseServerSwiftTests/AppTests.swift index 836f591d..5de4fc19 100644 --- a/Tests/ParseServerSwiftTests/AppTests.swift +++ b/Tests/ParseServerSwiftTests/AppTests.swift @@ -11,27 +11,38 @@ final class AppTests: XCTestCase { func setupAppForTesting(hookKey: String? = nil) async throws -> Application { let app = try await Application.make(.testing) - let configuration = try ParseServerConfiguration(app: app, - hostName: "hostName", - port: 8080, - applicationId: "applicationId", - primaryKey: "primaryKey", - webhookKey: hookKey, - parseServerURLString: "primaryKey") - try await ParseServerSwift.initialize(configuration, app: app, testing: true) + let configuration = try ParseServerConfiguration( + app: app, + hostName: "hostName", + port: 8080, + applicationId: "applicationId", + primaryKey: "primaryKey", + webhookKey: hookKey, + parseServerURLString: "primaryKey" + ) + try await ParseServerSwift.initialize( + configuration, + app: app, + testing: true + ) guard let parseServerURL = URL(string: configuration.primaryParseServerURLString) else { - throw ParseError(code: .otherCause, - message: "Could not make a URL from the Parse Server string") + let error = ParseError( + code: .otherCause, + message: "Could not make a URL from the Parse Server string" + ) + throw error } - try await ParseSwift.initialize(applicationId: configuration.applicationId, - primaryKey: configuration.primaryKey, - serverURL: parseServerURL, - usingPostForQuery: true, - requestCachePolicy: .reloadIgnoringLocalCacheData) { _, completionHandler in + try await ParseSwift.initialize( + applicationId: configuration.applicationId, + primaryKey: configuration.primaryKey, + serverURL: parseServerURL, + usingPostForQuery: true, + requestCachePolicy: .reloadIgnoringLocalCacheData + ) { _, completionHandler in // Setup to use default certificate pinning. See Parse-Swift docs for more info completionHandler(.performDefaultHandling, nil) } - try routes(app) + try exampleRoutes(app) return app } @@ -43,24 +54,28 @@ final class AppTests: XCTestCase { func testAllowInitConfigOnce() async throws { let app = try await Application.make(.testing) - let configuration = try ParseServerConfiguration(app: app, - hostName: "hostName", - port: 8080, - applicationId: "applicationId", - primaryKey: "primaryKey", - parseServerURLString: "primaryKey") + let configuration = try ParseServerConfiguration( + app: app, + hostName: "hostName", + port: 8080, + applicationId: "applicationId", + primaryKey: "primaryKey", + parseServerURLString: "primaryKey" + ) XCTAssertNoThrow(try setConfiguration(configuration)) try await app.asyncShutdown() } func testDoNotInitConfigTwice() async throws { let app = try await setupAppForTesting() - let configuration = try ParseServerConfiguration(app: app, - hostName: "hostName", - port: 8080, - applicationId: "applicationId", - primaryKey: "primaryKey", - parseServerURLString: "primaryKey") + let configuration = try ParseServerConfiguration( + app: app, + hostName: "hostName", + port: 8080, + applicationId: "applicationId", + primaryKey: "primaryKey", + parseServerURLString: "primaryKey" + ) XCTAssertThrowsError(try setConfiguration(configuration)) try await app.asyncShutdown() }