From a99bf31f59dba033888220afaeeb84835f3abd34 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 17 Mar 2021 20:02:24 -0400 Subject: [PATCH] Batch child objects using transactions (#94) * Batch child objects using transactions * codecov * clean up * nit * Add changelog * nits * Fixed bug linking anonymous user * prepare for release * remove old child object code * Update .codecov.yml --- .codecov.yml | 2 +- CHANGELOG.md | 11 +- .../Contents.swift | 43 ++--- ParseSwift.podspec | 2 +- ParseSwift.xcodeproj/project.pbxproj | 16 +- Scripts/jazzy.sh | 2 +- Sources/ParseSwift/API/API+Commands.swift | 175 +++++++---------- Sources/ParseSwift/API/BatchUtils.swift | 16 +- Sources/ParseSwift/API/Responses.swift | 39 +--- .../Protocols/ParseAuthentication.swift | 19 +- Sources/ParseSwift/Coding/AnyCodable.swift | 8 - Sources/ParseSwift/Coding/AnyEncodable.swift | 16 +- Sources/ParseSwift/Coding/ParseCoding.swift | 6 +- Sources/ParseSwift/Coding/ParseEncoder.swift | 12 +- Sources/ParseSwift/Objects/ParseObject.swift | 46 ++--- Sources/ParseSwift/Objects/ParseUser.swift | 181 +++++++++++------- Sources/ParseSwift/ParseConstants.swift | 2 +- .../ParseSwiftTests/ParseAnonymousTests.swift | 30 +-- Tests/ParseSwiftTests/ParseAppleTests.swift | 5 +- .../ParseAuthenticationTests.swift | 1 - .../ParseInstallationTests.swift | 1 - Tests/ParseSwiftTests/ParseLDAPTests.swift | 5 +- Tests/ParseSwiftTests/ParseObjectTests.swift | 22 ++- .../ParseSwiftTests/ParseOperationTests.swift | 1 - Tests/ParseSwiftTests/ParseUserTests.swift | 10 +- 25 files changed, 308 insertions(+), 363 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 2517c8e20..2422d6c36 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -5,7 +5,7 @@ coverage: status: patch: default: - target: 72 + target: 77 changes: false project: default: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f17b7d8c..3ed069d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,18 @@ # Parse-Swift Changelog ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.2.0...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.2.1...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 1.2.1 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.2.0...1.2.1) + +__Improvements__ +- Child objects are now automatically saved in batches using transactions. This will result in less network overhead and prevent uneccessary clean up of data on the server if a child objects throws an error while saving ([#94](https://github.com/parse-community/Parse-Swift/pull/94)), thanks to [Corey Baker](https://github.com/cbaker6). + +__Fixes__ +- There was a bug after a user first logs in anonymously and then becomes a real user as the server sends a new sessionToken when this occurs, but the SDK used the old sessionToken, resulting in an invalid sessionToken error ([#94](https://github.com/parse-community/Parse-Swift/pull/94)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 1.2.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.6...1.2.0) diff --git a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift index da4e380d7..210db700b 100644 --- a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift @@ -131,11 +131,28 @@ do { print("Error logging out: \(error)") } +//: Password Reset Request - synchronously. +do { + try User.verificationEmail(email: "hello@parse.org") + print("Successfully requested verification email be sent") +} catch let error { + print("Error requesting verification email be sent: \(error)") +} + +//: Password Reset Request - synchronously. +do { + try User.passwordReset(email: "hello@parse.org") + print("Successfully requested password reset") +} catch let error { + print("Error requesting password reset: \(error)") +} + //: Logging in anonymously. User.anonymous.login { result in switch result { case .success: print("Successfully logged in \(String(describing: User.current))") + print("Session token: \(String(describing: User.current?.sessionToken))") case .failure(let error): print("Error logging in: \(error)") } @@ -149,36 +166,12 @@ User.current?.signup { result in case .success(let user): print("Parse signup successful: \(user)") - + print("Session token: \(String(describing: User.current?.sessionToken))") case .failure(let error): print("Error logging in: \(error)") } } -//: Logging out - synchronously. -do { - try User.logout() - print("Successfully logged out") -} catch let error { - print("Error logging out: \(error)") -} - -//: Password Reset Request - synchronously. -do { - try User.verificationEmail(email: "hello@parse.org") - print("Successfully requested verification email be sent") -} catch let error { - print("Error requesting verification email be sent: \(error)") -} - -//: Password Reset Request - synchronously. -do { - try User.passwordReset(email: "hello@parse.org") - print("Successfully requested password reset") -} catch let error { - print("Error requesting password reset: \(error)") -} - PlaygroundPage.current.finishExecution() //: [Next](@next) diff --git a/ParseSwift.podspec b/ParseSwift.podspec index 09a584f74..28f5974bb 100644 --- a/ParseSwift.podspec +++ b/ParseSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ParseSwift" - s.version = "1.2.0" + s.version = "1.2.1" s.summary = "Parse Pure Swift SDK" s.homepage = "https://github.com/parse-community/Parse-Swift" s.authors = { diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index ccf92eb34..11da8acdd 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -2261,7 +2261,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.parse.ParseSwift; PRODUCT_NAME = ParseSwift; SKIP_INSTALL = YES; @@ -2285,7 +2285,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.parse.ParseSwift; PRODUCT_NAME = ParseSwift; SKIP_INSTALL = YES; @@ -2351,7 +2351,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.parse.ParseSwift; PRODUCT_NAME = ParseSwift; SDKROOT = macosx; @@ -2377,7 +2377,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.parse.ParseSwift; PRODUCT_NAME = ParseSwift; SDKROOT = macosx; @@ -2524,7 +2524,7 @@ INFOPLIST_FILE = "ParseSwift-watchOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.parse.ParseSwift-watchOS"; @@ -2553,7 +2553,7 @@ INFOPLIST_FILE = "ParseSwift-watchOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.parse.ParseSwift-watchOS"; PRODUCT_NAME = ParseSwift; @@ -2580,7 +2580,7 @@ INFOPLIST_FILE = "ParseSwift-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.parse.ParseSwift-tvOS"; @@ -2608,7 +2608,7 @@ INFOPLIST_FILE = "ParseSwift-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.2.1; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.parse.ParseSwift-tvOS"; PRODUCT_NAME = ParseSwift; diff --git a/Scripts/jazzy.sh b/Scripts/jazzy.sh index 46d9a287c..d2c4bb938 100755 --- a/Scripts/jazzy.sh +++ b/Scripts/jazzy.sh @@ -5,7 +5,7 @@ bundle exec jazzy \ --author_url http://parseplatform.org \ --github_url https://github.com/parse-community/Parse-Swift \ --root-url http://parseplatform.org/Parse-Swift/api/ \ - --module-version 1.2.0 \ + --module-version 1.2.1 \ --theme fullwidth \ --skip-undocumented \ --output ./docs/api \ diff --git a/Sources/ParseSwift/API/API+Commands.swift b/Sources/ParseSwift/API/API+Commands.swift index 754d02ef7..bcaf81495 100644 --- a/Sources/ParseSwift/API/API+Commands.swift +++ b/Sources/ParseSwift/API/API+Commands.swift @@ -414,7 +414,7 @@ extension API.Command where T: ParseObject { } static func batch(commands: [API.Command], transaction: Bool) -> RESTBatchCommandType { - let commands = commands.compactMap { (command) -> API.Command? in + let batchCommands = commands.compactMap { (command) -> API.Command? in let path = ParseConfiguration.mountPath + command.path.urlComponent guard let body = command.body else { return nil @@ -423,22 +423,16 @@ extension API.Command where T: ParseObject { body: body, mapper: command.mapper) } - let bodies = commands.compactMap { (command) -> (body: T, command: API.Method)? in - guard let body = command.body else { - return nil - } - return (body: body, command: command.method) - } - let mapper = { (data: Data) -> [Result] in let decodingType = [BatchResponseItem].self do { let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data) - return bodies.enumerated().map({ (object) -> (Result) in + return commands.enumerated().map({ (object) -> (Result) in let response = responses[object.offset] - if let success = response.success { - return .success(success.apply(to: object.element.body, method: object.element.command)) + if let success = response.success, + let body = object.element.body { + return .success(success.apply(to: body, method: object.element.method)) } else { guard let parseError = response.error else { return .failure(ParseError(code: .unknownError, message: "unknown error")) @@ -455,7 +449,7 @@ extension API.Command where T: ParseObject { } } - let batchCommand = BatchCommand(requests: commands, transaction: transaction) + let batchCommand = BatchCommand(requests: batchCommands, transaction: transaction) return RESTBatchCommandType(method: .POST, path: .batch, body: batchCommand, mapper: mapper) } @@ -499,41 +493,49 @@ extension API.Command where T: ParseObject { } } -//This has been disabled, looking into getting it working in the future. -//It's only needed for sending batches of childObjects which currently isn't being used. -/* // MARK: Batch - Child Objects -extension API.ChildCommand { +extension API.NonParseBodyCommand { internal var data: Data? { guard let body = body else { return nil } return try? ParseCoding.jsonEncoder().encode(body) } - static func batch(commands: [API.ChildCommand], - transaction: Bool) -> RESTBatchCommandTypeEncodable { - let commands = commands.compactMap { (command) -> API.ChildCommand? in - let path = ParseConfiguration.mountPath + command.path.urlComponent - guard let body = command.body else { + static func batch(objects: [ParseType], + transaction: Bool) throws -> RESTBatchCommandTypeEncodable { + let batchCommands = try objects.compactMap { (object) -> API.BatchCommand? in + guard var objectable = object as? Objectable else { return nil } - return API.ChildCommand(method: command.method, path: .any(path), - body: body, mapper: command.mapper) - } - let bodies = commands.compactMap { (command) -> (body: ParseType, command: API.Method)? in - guard let body = command.body else { - return nil + let method: API.Method! + if objectable.isSaved { + method = .PUT + } else { + method = .POST } - return (body: body, command: command.method) + + let mapper = { (baseObjectable: BaseObjectable) throws -> PointerType in + objectable.objectId = baseObjectable.objectId + return try objectable.toPointer() + } + + let path = ParseConfiguration.mountPath + objectable.endpoint.urlComponent + let encoded = try ParseCoding.parseEncoder().encode(object) + let body = try ParseCoding.jsonDecoder().decode(AnyCodable.self, from: encoded) + return API.BatchCommand(method: method, + path: .any(path), + body: body, + mapper: mapper) } + let mapper = { (data: Data) -> [Result] in - let decodingType = [BatchResponseItem].self + let decodingType = [BatchResponseItem].self do { let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data) - return bodies.enumerated().map({ (object) -> (Result) in + return batchCommands.enumerated().map({ (object) -> (Result) in let response = responses[object.offset] if let success = response.success { - guard let successfulResponse = try? success.apply(to: object.element.body) else { + guard let successfulResponse = try? object.element.mapper(success) else { return.failure(ParseError(code: .unknownError, message: "unknown error")) } return .success(successfulResponse) @@ -552,11 +554,15 @@ extension API.ChildCommand { return [(.failure(parseError))] } } - let batchCommand = BatchCommand(requests: commands, transaction: transaction) - return RESTBatchCommandTypeEncodable(method: .POST, path: .batch, body: batchCommand, mapper: mapper) + let batchCommand = BatchChildCommand(requests: batchCommands, + transaction: transaction) + return RESTBatchCommandTypeEncodable(method: .POST, + path: .batch, + body: batchCommand, + mapper: mapper) } } -*/ + // MARK: API.NonParseBodyCommand internal extension API { struct NonParseBodyCommand: Encodable where T: Encodable { @@ -565,7 +571,6 @@ internal extension API { let path: API.Endpoint let body: T? let mapper: ((Data) throws -> U) - let params: [String: String?]? init(method: API.Method, path: API.Endpoint, @@ -576,7 +581,6 @@ internal extension API { self.path = path self.body = body self.mapper = mapper - self.params = params } func execute(options: API.Options) throws -> U { @@ -618,23 +622,17 @@ internal extension API { // MARK: URL Preperation func prepareURLRequest(options: API.Options) -> Result { - let params = self.params?.getQueryItems() var headers = API.getHeaders(options: options) if !(method == .POST) && !(method == .PUT) { headers.removeValue(forKey: "X-Parse-Request-Id") } let url = ParseConfiguration.serverURL.appendingPathComponent(path.urlComponent) - guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), + let urlComponents = components.url else { return .failure(ParseError(code: .unknownError, message: "couldn't unrwrap url components for \(url)")) } - components.queryItems = params - - guard let urlComponents = components.url else { - return .failure(ParseError(code: .unknownError, - message: "couldn't create url from components for \(components)")) - } var urlRequest = URLRequest(url: urlComponents) urlRequest.allHTTPHeaderFields = headers @@ -657,96 +655,51 @@ internal extension API { } internal extension API.NonParseBodyCommand { + // MARK: Deleting static func deleteCommand(_ object: T) throws -> API.NonParseBodyCommand where T: ParseObject { guard object.isSaved else { - throw ParseError(code: .unknownError, message: "Cannot Delete an object without id") + throw ParseError(code: .unknownError, + message: "Cannot delete an object without an objectId") } - return API.NonParseBodyCommand( - method: .DELETE, - path: object.endpoint - ) { (data) -> NoBody in - let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) - if let error = error { + let mapper = { (data: Data) -> NoBody in + if let error = try? ParseCoding + .jsonDecoder() + .decode(ParseError.self, + from: data) { throw error } else { return NoBody() } } + + return API.NonParseBodyCommand(method: .DELETE, + path: object.endpoint, + mapper: mapper) } } -/* -// MARK: API.Command + internal extension API { - struct ChildCommand: ParseType { - typealias ReturnType = U + struct BatchCommand: Encodable where T: Encodable { + typealias ReturnType = U // swiftlint:disable:this nesting let method: API.Method let path: API.Endpoint - let body: ParseType? - let mapper: ((Data) throws -> U) - let params: [String: String?]? + let body: T? + let mapper: ((BaseObjectable) throws -> U) init(method: API.Method, path: API.Endpoint, - params: [String: String]? = nil, - body: ParseType? = nil, - mapper: @escaping ((Data) throws -> U)) { + body: T? = nil, + mapper: @escaping ((BaseObjectable) throws -> U)) { self.method = method self.path = path self.body = body self.mapper = mapper - self.params = params - } - } - - enum CodingKeys: String, CodingKey { - case method, body, path - } -} - -internal extension API.ChildCommand { - // MARK: Saving ParseObjects - Encodable - static func saveCommand(_ object: ParseType) throws -> API.ChildCommand { - guard let objectable = object as? Objectable else { - throw ParseError(code: .unknownError, message: "Not able to cast to objectable. Not saving") } - if objectable.isSaved { - return try updateCommand(object) - } else { - return try createCommand(object) - } - } - // MARK: Saving ParseObjects - Encodable - private - private static func createCommand(_ object: ParseType) throws -> API.ChildCommand { - guard var objectable = object as? Objectable else { - throw ParseError(code: .unknownError, message: "Not able to cast to objectable. Not saving") - } - let mapper = { (data: Data) -> PointerType in - let baseObjectable = try ParseCoding.jsonDecoder().decode(BaseObjectable.self, from: data) - objectable.objectId = baseObjectable.objectId - return try objectable.toPointer() - } - return API.ChildCommand(method: .POST, - path: objectable.endpoint, - body: object, - mapper: mapper) - } - - private static func updateCommand(_ object: ParseType) throws -> API.ChildCommand { - guard var objectable = object as? Objectable else { - throw ParseError(code: .unknownError, message: "Not able to cast to objectable. Not saving") - } - let mapper = { (data: Data) -> PointerType in - let baseObjectable = try ParseCoding.jsonDecoder().decode(BaseObjectable.self, from: data) - objectable.objectId = baseObjectable.objectId - return try objectable.toPointer() + enum CodingKeys: String, CodingKey { // swiftlint:disable:this nesting + case method, body, path } - return API.ChildCommand(method: .PUT, - path: objectable.endpoint, - body: object, - mapper: mapper) } -}// swiftlint:disable:this file_length -*/ +} diff --git a/Sources/ParseSwift/API/BatchUtils.swift b/Sources/ParseSwift/API/BatchUtils.swift index 4c2c39db6..77253e97c 100644 --- a/Sources/ParseSwift/API/BatchUtils.swift +++ b/Sources/ParseSwift/API/BatchUtils.swift @@ -16,13 +16,13 @@ typealias RESTBatchCommandType = API.Command, Pars typealias ParseObjectBatchCommandNoBody = BatchCommandNoBody typealias ParseObjectBatchResponseNoBody = [(Result)] typealias RESTBatchCommandNoBodyType = API.NonParseBodyCommand, ParseObjectBatchResponseNoBody> where T: Encodable -/* -typealias ParseObjectBatchCommandEncodable = BatchCommand where T: ParseType + +typealias ParseObjectBatchCommandEncodable = BatchChildCommand where T: Encodable typealias ParseObjectBatchResponseEncodable = [(Result)] // swiftlint:disable line_length -typealias RESTBatchCommandTypeEncodable = API.Command, ParseObjectBatchResponseEncodable> where T: ParseType +typealias RESTBatchCommandTypeEncodable = API.NonParseBodyCommand, ParseObjectBatchResponseEncodable> where T: Encodable // swiftlint:enable line_length -*/ + internal struct BatchCommand: ParseType where T: ParseType { let requests: [API.Command] var transaction: Bool @@ -32,12 +32,12 @@ internal struct BatchCommandNoBody: Encodable where T: Encodable { let requests: [API.NonParseBodyCommand] var transaction: Bool } -/* -internal struct BatchChildCommand: ParseType { - let requests: [API.ChildCommand] + +internal struct BatchChildCommand: Encodable where T: Encodable { + let requests: [API.BatchCommand] var transaction: Bool } -*/ + struct BatchUtils { static func splitArray(_ array: [U], valuesPerSegment: Int) -> [[U]] { if array.count < valuesPerSegment { diff --git a/Sources/ParseSwift/API/Responses.swift b/Sources/ParseSwift/API/Responses.swift index 0ea3e3d79..d881a1a96 100644 --- a/Sources/ParseSwift/API/Responses.swift +++ b/Sources/ParseSwift/API/Responses.swift @@ -8,40 +8,6 @@ import Foundation -protocol ChildResponse: Codable { - var objectId: String { get set } - var className: String { get set } -} - -// MARK: ParseObject -internal struct PointerSaveResponse: ChildResponse { - - private let __type: String = "Pointer" // swiftlint:disable:this identifier_name - public var objectId: String - public var className: String - - public init?(_ target: Objectable) { - guard let objectId = target.objectId else { - return nil - } - self.objectId = objectId - self.className = target.className - } - - private enum CodingKeys: String, CodingKey { - case __type, objectId, className // swiftlint:disable:this identifier_name - } - - func apply(to object: T) throws -> PointerType where T: Encodable { - guard let object = object as? Objectable else { - throw ParseError(code: .unknownError, message: "Should have converted encoded object to Pointer") - } - var pointer = try PointerType(object) - pointer.objectId = objectId - return pointer - } -} - internal struct SaveResponse: Decodable { var objectId: String var createdAt: Date @@ -58,6 +24,11 @@ internal struct SaveResponse: Decodable { } } +internal struct UpdateSessionTokenResponse: Decodable { + var updatedAt: Date + let sessionToken: String +} + internal struct UpdateResponse: Decodable { var updatedAt: Date diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift index 5e4a60ddf..18888e3ee 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift @@ -240,12 +240,23 @@ public extension ParseUser { completion: @escaping (Result) -> Void) { let body = SignupLoginBody(authData: [type: authData]) - signupCommand(body: body) - .executeAsync(options: options) { result in - callbackQueue.async { - completion(result) + do { + try signupCommand(body: body) + .executeAsync(options: options) { result in + callbackQueue.async { + completion(result) + } + } + } catch { + callbackQueue.async { + if let parseError = error as? ParseError { + completion(.failure(parseError)) + } else { + let parseError = ParseError(code: .unknownError, message: error.localizedDescription) + completion(.failure(parseError)) } } + } } // MARK: 3rd Party Authentication - Link diff --git a/Sources/ParseSwift/Coding/AnyCodable.swift b/Sources/ParseSwift/Coding/AnyCodable.swift index d631cf700..684e9bf45 100755 --- a/Sources/ParseSwift/Coding/AnyCodable.swift +++ b/Sources/ParseSwift/Coding/AnyCodable.swift @@ -16,18 +16,10 @@ import Foundation Source: https://github.com/Flight-School/AnyCodable */ public struct AnyCodable: Codable { - public typealias DateEncodingStrategy = (Date, Encoder) throws -> Void - public let dateEncodingStrategy: DateEncodingStrategy? public let value: Any public init(_ value: T?) { - self.dateEncodingStrategy = nil - self.value = value ?? () - } - - public init(_ value: T?, dateEncodingStrategy: DateEncodingStrategy?) { - self.dateEncodingStrategy = dateEncodingStrategy self.value = value ?? () } } diff --git a/Sources/ParseSwift/Coding/AnyEncodable.swift b/Sources/ParseSwift/Coding/AnyEncodable.swift index c987bf280..bc20322d6 100755 --- a/Sources/ParseSwift/Coding/AnyEncodable.swift +++ b/Sources/ParseSwift/Coding/AnyEncodable.swift @@ -29,23 +29,15 @@ import Foundation Source: https://github.com/Flight-School/AnyCodable */ public struct AnyEncodable: Encodable { - public let dateEncodingStrategy: AnyCodable.DateEncodingStrategy? public let value: Any - public init(_ value: T?, dateEncodingStrategy: AnyCodable.DateEncodingStrategy?) { - self.dateEncodingStrategy = dateEncodingStrategy - self.value = value ?? () - } - public init(_ value: T?) { - self.dateEncodingStrategy = nil self.value = value ?? () } } @usableFromInline protocol _AnyEncodable { - var dateEncodingStrategy: AnyCodable.DateEncodingStrategy? { get } var value: Any { get } init(_ value: T?) @@ -58,17 +50,13 @@ extension AnyEncodable: _AnyEncodable {} extension _AnyEncodable { // swiftlint:disable:next cyclomatic_complexity function_body_length public func encode(to encoder: Encoder) throws { - if let date = self.value as? Date, let strategy = dateEncodingStrategy { - try strategy(date, encoder) - return - } var container = encoder.singleValueContainer() switch self.value { case let dictionary as [String: Any?]: - try container.encode(dictionary.mapValues { AnyCodable($0, dateEncodingStrategy: dateEncodingStrategy) }) + try container.encode(dictionary.mapValues { AnyCodable($0) }) case let array as [Any?]: - try container.encode(array.map { AnyCodable($0, dateEncodingStrategy: dateEncodingStrategy) }) + try container.encode(array.map { AnyCodable($0) }) case let url as URL: try container.encode(url) case let string as String: diff --git a/Sources/ParseSwift/Coding/ParseCoding.swift b/Sources/ParseSwift/Coding/ParseCoding.swift index 2005e6f45..4fe973614 100644 --- a/Sources/ParseSwift/Coding/ParseCoding.swift +++ b/Sources/ParseSwift/Coding/ParseCoding.swift @@ -18,7 +18,7 @@ extension ParseCoding { /// strategy for `Parse`. static func jsonEncoder() -> JSONEncoder { let encoder = JSONEncoder() - encoder.dateEncodingStrategy = jsonDateEncodingStrategy + encoder.dateEncodingStrategy = parseDateEncodingStrategy return encoder } @@ -54,9 +54,7 @@ extension ParseCoding { return dateFormatter }() - static let jsonDateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .custom(parseDateEncodingStrategy) - - static let parseDateEncodingStrategy: AnyCodable.DateEncodingStrategy = { (date, encoder) in + static let parseDateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .custom { (date, encoder) in var container = encoder.container(keyedBy: DateEncodingKeys.self) try container.encode("Date", forKey: .type) let dateString = dateFormatter.string(from: date) diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift index b7004d63e..71e2eea94 100644 --- a/Sources/ParseSwift/Coding/ParseEncoder.swift +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -50,7 +50,7 @@ extension Dictionary: _JSONStringDictionaryEncodableMarker where Key == String, // MARK: ParseEncoder public struct ParseEncoder { - let dateEncodingStrategy: AnyCodable.DateEncodingStrategy? + let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy? public enum SkippedKeys { case object @@ -74,7 +74,7 @@ public struct ParseEncoder { } init( - dateEncodingStrategy: AnyCodable.DateEncodingStrategy? = nil + dateEncodingStrategy: JSONEncoder.DateEncodingStrategy? = nil ) { self.dateEncodingStrategy = dateEncodingStrategy } @@ -82,7 +82,7 @@ public struct ParseEncoder { func encode(_ value: Encodable) throws -> Data { let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: SkippedKeys.none.keys()) if let dateEncodingStrategy = dateEncodingStrategy { - encoder.dateEncodingStrategy = .custom(dateEncodingStrategy) + encoder.dateEncodingStrategy = dateEncodingStrategy } return try encoder.encodeObject(value, collectChildren: false, objectsSavedBeforeThisOne: nil, filesSavedBeforeThisOne: nil).encoded } @@ -90,7 +90,7 @@ public struct ParseEncoder { public func encode(_ value: T, skipKeys: SkippedKeys) throws -> Data { let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: skipKeys.keys()) if let dateEncodingStrategy = dateEncodingStrategy { - encoder.dateEncodingStrategy = .custom(dateEncodingStrategy) + encoder.dateEncodingStrategy = dateEncodingStrategy } return try encoder.encodeObject(value, collectChildren: false, objectsSavedBeforeThisOne: nil, filesSavedBeforeThisOne: nil).encoded } @@ -101,7 +101,7 @@ public struct ParseEncoder { filesSavedBeforeThisOne: [UUID: ParseFile]?) throws -> (encoded: Data, unique: Set, unsavedChildren: [Encodable]) { let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: SkippedKeys.object.keys()) if let dateEncodingStrategy = dateEncodingStrategy { - encoder.dateEncodingStrategy = .custom(dateEncodingStrategy) + encoder.dateEncodingStrategy = dateEncodingStrategy } return try encoder.encodeObject(value, collectChildren: true, objectsSavedBeforeThisOne: objectsSavedBeforeThisOne, filesSavedBeforeThisOne: filesSavedBeforeThisOne) } @@ -112,7 +112,7 @@ public struct ParseEncoder { filesSavedBeforeThisOne: [UUID: ParseFile]?) throws -> (encoded: Data, unique: Set, unsavedChildren: [Encodable]) { let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: SkippedKeys.object.keys()) if let dateEncodingStrategy = dateEncodingStrategy { - encoder.dateEncodingStrategy = .custom(dateEncodingStrategy) + encoder.dateEncodingStrategy = dateEncodingStrategy } return try encoder.encodeObject(value, collectChildren: collectChildren, objectsSavedBeforeThisOne: objectsSavedBeforeThisOne, filesSavedBeforeThisOne: filesSavedBeforeThisOne) } diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 9a52e1475..4b1509bdb 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -707,22 +707,14 @@ extension ParseObject { return } - //Currently, batch isn't working for Encodable - /*let savedChildObjects = try Self.saveAll(objects: savableObjects, - options: options) - let savedChildPointers = try savedChildObjects.compactMap { try $0.get() } - if savedChildPointers.count != savableObjects.count { - throw ParseError(code: .unknownError, message: "Couldn't save all child objects") - } - for (index, object) in savableObjects.enumerated() { - let hash = try BaseObjectable.createHash(object) - objectsFinishedSaving[hash] = savedChildPointers[index] - }*/ - - //Saving children individually - try savableObjects.forEach { - let hash = try BaseObjectable.createHash($0) - objectsFinishedSaving[hash] = try $0.save(options: options) + if savableObjects.count > 0 { + let savedChildObjects = try self.saveAll(objects: savableObjects, + options: options) + let savedChildPointers = try savedChildObjects.compactMap { try $0.get() } + for (index, object) in savableObjects.enumerated() { + let hash = try BaseObjectable.createHash(object) + objectsFinishedSaving[hash] = savedChildPointers[index] + } } try savableFiles.forEach { @@ -746,28 +738,14 @@ extension ParseObject { // MARK: Savable Encodable Version internal extension ParseType { - func save(options: API.Options = []) throws -> PointerType { - try saveCommand() - .execute(options: options, - callbackQueue: .main) - } - - func saveCommand() throws -> API.Command { - try API.Command.saveCommand(self) - } - /* func saveAll(objects: [ParseType], transaction: Bool = true, options: API.Options = []) throws -> [(Result)] { - let commands = try objects.map { - try API.ChildCommand.saveCommand($0) - } - return try API.ChildCommand - .batch(commands: commands, + try API.NonParseBodyCommand + .batch(objects: objects, transaction: transaction) - .execute(options: options, - callbackQueue: .main) - }*/ + .execute(options: options) + } } // MARK: Deletable diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index ff5317830..d6a222fc1 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -121,11 +121,7 @@ extension ParseUser { public static var current: Self? { get { Self.currentUserContainer?.currentUser } set { - if Self.currentUserContainer?.currentUser?.username != newValue?.username && newValue != nil { - Self.currentUserContainer?.currentUser = newValue?.anonymous.strip(newValue!) - } else { - Self.currentUserContainer?.currentUser = newValue - } + Self.currentUserContainer?.currentUser = newValue } } @@ -479,13 +475,8 @@ extension ParseUser { */ public static func signup(username: String, password: String, options: API.Options = []) throws -> Self { - if Self.current != nil { - Self.current!.username = username - Self.current!.password = password - Self.current!.anonymous.strip() - return try Self.current!.save(options: options) - } - return try signupCommand(body: SignupLoginBody(username: username, password: password)) + try signupCommand(body: SignupLoginBody(username: username, + password: password)) .execute(options: options) } @@ -499,14 +490,8 @@ extension ParseUser { - returns: Returns whether the sign up was successful. */ public func signup(options: API.Options = []) throws -> Self { - if let current = Self.current { - if !current.anonymous.isLinked { - return try current.save(options: options) - } else { - throw ParseError(code: .usernameTaken, message: "Cannot sign up a user that has already signed up.") - } - } - return try signupCommand().execute(options: options, callbackQueue: .main) + try signupCommand().execute(options: options, + callbackQueue: .main) } /** @@ -522,21 +507,22 @@ extension ParseUser { */ public func signup(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { - if let current = Self.current { - if !current.anonymous.isLinked { - current.save(options: options, callbackQueue: callbackQueue, completion: completion) - } else { - let error = ParseError(code: .usernameTaken, - message: "Cannot sign up a user that has already signed up.") - completion(.failure(error)) + do { + try signupCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue) { result in + callbackQueue.async { + completion(result) + } } - return - } - signupCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue) { result in + } catch { callbackQueue.async { - completion(result) + if let parseError = error as? ParseError { + completion(.failure(parseError)) + } else { + let parseError = ParseError(code: .unknownError, message: error.localizedDescription) + completion(.failure(parseError)) + } } } } @@ -560,59 +546,124 @@ extension ParseUser { options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { - if Self.current != nil { - Self.current!.username = username - Self.current!.password = password - Self.current!.anonymous.strip() - Self.current!.save(options: options, callbackQueue: callbackQueue, completion: completion) - return - } let body = SignupLoginBody(username: username, password: password) - signupCommand(body: body) - .executeAsync(options: options) { result in + do { + try signupCommand(body: body) + .executeAsync(options: options) { result in callbackQueue.async { completion(result) } } + } catch { + callbackQueue.async { + if let parseError = error as? ParseError { + completion(.failure(parseError)) + } else { + let parseError = ParseError(code: .unknownError, message: error.localizedDescription) + completion(.failure(parseError)) + } + } + } } - internal static func signupCommand(body: SignupLoginBody) -> API.NonParseBodyCommand { + internal static func signupCommand(body: SignupLoginBody) throws -> API.NonParseBodyCommand { + + var method = API.Method.POST + var path = API.Endpoint.users + if let current = Self.current { + if current.anonymous.isLinked { + Self.current!.anonymous.strip() + method = .PUT + path = current.endpoint + } else { + throw ParseError(code: .usernameTaken, + message: "Cannot sign up a user that has already signed up.") + } + } - return API.NonParseBodyCommand(method: .POST, path: .users, body: body) { (data) -> Self in + return API.NonParseBodyCommand(method: method, path: path, body: body) { (data) -> Self in + var user: Self! + var sessionToken: String! - let response = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data) - var user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) + if method == .POST { + sessionToken = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data).sessionToken + user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) - if user.username == nil { - if let username = body.username { - user.username = username + if user.username == nil { + if let username = body.username { + user.username = username + } } - } - if user.authData == nil { - if let authData = body.authData { - user.authData = authData + if user.authData == nil { + if let authData = body.authData { + user.authData = authData + } + } + } else { + if let currentUser = Self.current { + let response = try ParseCoding.jsonDecoder().decode(UpdateSessionTokenResponse.self, from: data) + user = currentUser + if user.authData == nil { + user.authData = body.authData + } else { + if user.authData != body.authData { + if let authData = body.authData { + for (key, value) in authData { + user.authData![key] = value + } + } + } + } + user.updatedAt = response.updatedAt + sessionToken = response.sessionToken + } else { + throw ParseError(code: .usernameTaken, + message: "Cannot link user when current user is not logged in.") } } - - Self.currentUserContainer = .init( - currentUser: user, - sessionToken: response.sessionToken - ) + Self.currentUserContainer = .init(currentUser: user, + sessionToken: sessionToken) Self.saveCurrentContainerToKeychain() return user } } - internal func signupCommand() -> API.Command { - return API.Command(method: .POST, path: .users, body: self) { (data) -> Self in - - let response = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data) - var user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) - user.username = self.username + internal func signupCommand() throws -> API.Command { + var method = API.Method.POST + if let currentUser = Self.current { + if currentUser.anonymous.isLinked { + Self.current!.anonymous.strip() + method = .PUT + } else { + throw ParseError(code: .usernameTaken, + message: "Cannot sign up a user that has already signed up.") + } + } + return API.Command(method: method, + path: endpoint, + body: self) { (data) -> Self in + var user: Self! + var sessionToken: String! + + if method == .POST { + sessionToken = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data).sessionToken + user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) + user.username = self.username + } else { + if let currentUser = Self.current { + let response = try ParseCoding.jsonDecoder().decode(UpdateSessionTokenResponse.self, from: data) + user = currentUser + user.updatedAt = response.updatedAt + sessionToken = response.sessionToken + } else { + throw ParseError(code: .usernameTaken, + message: "Cannot link user when current user is not logged in.") + } + } Self.currentUserContainer = .init( currentUser: user, - sessionToken: response.sessionToken + sessionToken: sessionToken ) Self.saveCurrentContainerToKeychain() return user diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index bd9d67ca0..0024448c6 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -9,7 +9,7 @@ import Foundation enum ParseConstants { - static let parseVersion = "1.2.0" + static let parseVersion = "1.2.1" static let hashingKey = "parseSwift" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" diff --git a/Tests/ParseSwiftTests/ParseAnonymousTests.swift b/Tests/ParseSwiftTests/ParseAnonymousTests.swift index a194f8df5..62b8a3608 100644 --- a/Tests/ParseSwiftTests/ParseAnonymousTests.swift +++ b/Tests/ParseSwiftTests/ParseAnonymousTests.swift @@ -57,6 +57,11 @@ class ParseAnonymousTests: XCTestCase { } } + struct UpdateSessionTokenResponse: Codable { + var updatedAt: Date + let sessionToken: String + } + override func setUpWithError() throws { try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { @@ -210,26 +215,23 @@ class ParseAnonymousTests: XCTestCase { wait(for: [expectation1], timeout: 20.0) } - func testReplaceAnonymousWithUsernameChange() throws { - let expectedAuth = ["id": "yolo"] - var user = try loginNormally() - user.authData = [user.anonymous.__type: expectedAuth] - User.current = user - XCTAssertEqual(user, User.current) + func testReplaceAnonymousUser() throws { + try testLogin() + guard let user = User.current, + let updatedAt = user.updatedAt else { + XCTFail("Shold have unwrapped") + return + } XCTAssertTrue(user.anonymous.isLinked) - //Convert the anonymous user to a real new user. - User.current?.username = "hello" - User.current?.password = "world" - User.current?.authData = [user.anonymous.__type: nil] - var userOnServer = User.current! - userOnServer.updatedAt = user.updatedAt?.addingTimeInterval(+300) + var response = UpdateSessionTokenResponse(updatedAt: updatedAt.addingTimeInterval(+300), + sessionToken: "blast") let encoded: Data! do { - encoded = try userOnServer.getEncoder().encode(userOnServer, skipKeys: .none) + encoded = try ParseCoding.jsonEncoder().encode(response) //Get dates in correct format from ParseDecoding strategy - userOnServer = try userOnServer.getDecoder().decode(User.self, from: encoded) + response = try ParseCoding.jsonDecoder().decode(UpdateSessionTokenResponse.self, from: encoded) } catch { XCTFail("Should encode/decode. Error \(error)") return diff --git a/Tests/ParseSwiftTests/ParseAppleTests.swift b/Tests/ParseSwiftTests/ParseAppleTests.swift index e57703849..9cc921c3b 100644 --- a/Tests/ParseSwiftTests/ParseAppleTests.swift +++ b/Tests/ParseSwiftTests/ParseAppleTests.swift @@ -213,7 +213,8 @@ class ParseAppleTests: XCTestCase { serverResponse.password = "world" serverResponse.objectId = "yarr" serverResponse.sessionToken = "myToken" - serverResponse.authData = [serverResponse.apple.__type: authData] + serverResponse.authData = [serverResponse.apple.__type: authData, + serverResponse.anonymous.__type: nil] serverResponse.createdAt = Date() serverResponse.updatedAt = serverResponse.createdAt?.addingTimeInterval(+300) @@ -239,7 +240,7 @@ class ParseAppleTests: XCTestCase { case .success(let user): XCTAssertEqual(user, User.current) - XCTAssertEqual(user, userOnServer) + XCTAssertEqual(user.authData, userOnServer.authData) XCTAssertEqual(user.username, "hello") XCTAssertEqual(user.password, "world") XCTAssertTrue(user.apple.isLinked) diff --git a/Tests/ParseSwiftTests/ParseAuthenticationTests.swift b/Tests/ParseSwiftTests/ParseAuthenticationTests.swift index e7d95475f..c304fea3a 100644 --- a/Tests/ParseSwiftTests/ParseAuthenticationTests.swift +++ b/Tests/ParseSwiftTests/ParseAuthenticationTests.swift @@ -105,7 +105,6 @@ class ParseAuthenticationTests: XCTestCase { XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/users/\(objectId)") XCTAssertEqual(command.method, API.Method.PUT) - XCTAssertNil(command.params) XCTAssertNotNil(command.body) XCTAssertEqual(command.body?.authData, body.authData) } diff --git a/Tests/ParseSwiftTests/ParseInstallationTests.swift b/Tests/ParseSwiftTests/ParseInstallationTests.swift index 827dc976c..587d2e945 100644 --- a/Tests/ParseSwiftTests/ParseInstallationTests.swift +++ b/Tests/ParseSwiftTests/ParseInstallationTests.swift @@ -668,7 +668,6 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/installations/\(objectId)") XCTAssertEqual(command.method, API.Method.DELETE) - XCTAssertNil(command.params) XCTAssertNil(command.body) } catch { XCTFail(error.localizedDescription) diff --git a/Tests/ParseSwiftTests/ParseLDAPTests.swift b/Tests/ParseSwiftTests/ParseLDAPTests.swift index aa6953e99..ceee48d86 100644 --- a/Tests/ParseSwiftTests/ParseLDAPTests.swift +++ b/Tests/ParseSwiftTests/ParseLDAPTests.swift @@ -199,7 +199,8 @@ class ParseLDAPTests: XCTestCase { serverResponse.password = "world" serverResponse.objectId = "yarr" serverResponse.sessionToken = "myToken" - serverResponse.authData = [serverResponse.ldap.__type: authData] + serverResponse.authData = [serverResponse.ldap.__type: authData, + serverResponse.anonymous.__type: nil] serverResponse.createdAt = Date() serverResponse.updatedAt = serverResponse.createdAt?.addingTimeInterval(+300) @@ -225,7 +226,7 @@ class ParseLDAPTests: XCTestCase { case .success(let user): XCTAssertEqual(user, User.current) - XCTAssertEqual(user, userOnServer) + XCTAssertEqual(user.authData, userOnServer.authData) XCTAssertEqual(user.username, "hello") XCTAssertEqual(user.password, "world") XCTAssertTrue(user.ldap.isLinked) diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index 8afca1e53..86fbab376 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -901,7 +901,6 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/classes/\(className)/\(objectId)") XCTAssertEqual(command.method, API.Method.DELETE) - XCTAssertNil(command.params) XCTAssertNil(command.body) } catch { XCTFail(error.localizedDescription) @@ -1118,11 +1117,15 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length scoreOnServer.updatedAt = scoreOnServer.createdAt scoreOnServer.ACL = nil scoreOnServer.objectId = "yarr" + + let response = [BatchResponseItem(success: scoreOnServer, error: nil)] + let encoded: Data! do { - encoded = try scoreOnServer.getEncoder().encode(scoreOnServer, skipKeys: .none) + encoded = try scoreOnServer.getJSONEncoder().encode(response) //Get dates in correct format from ParseDecoding strategy - scoreOnServer = try scoreOnServer.getDecoder().decode(GameScore.self, from: encoded) + let encodedScoreOnServer = try scoreOnServer.getEncoder().encode(scoreOnServer) + scoreOnServer = try scoreOnServer.getDecoder().decode(GameScore.self, from: encodedScoreOnServer) } catch { XCTFail("Should encode/decode. Error \(error)") return @@ -1231,9 +1234,11 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length levelOnServer.ACL = nil levelOnServer.objectId = "yarr" let pointer = try levelOnServer.toPointer() + + let response = [BatchResponseItem>(success: pointer, error: nil)] let encoded: Data! do { - encoded = try ParseCoding.jsonEncoder().encode(pointer) + encoded = try ParseCoding.jsonEncoder().encode(response) } catch { XCTFail("Should encode/decode. Error \(error)") return @@ -1283,6 +1288,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length scoreOnServer.updatedAt = scoreOnServer.createdAt scoreOnServer.ACL = nil scoreOnServer.objectId = "yarr" + let encoded: Data! do { encoded = try scoreOnServer.getEncoder().encode(scoreOnServer, skipKeys: .none) @@ -1341,11 +1347,11 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length let parseFile = ParseFile(name: "profile.svg", cloudURL: cloudPath) game.profilePicture = parseFile - let response = FileUploadResponse(name: "89d74fcfa4faa5561799e5076593f67c_\(parseFile.name)", url: parseURL) + let fileResponse = FileUploadResponse(name: "89d74fcfa4faa5561799e5076593f67c_\(parseFile.name)", url: parseURL) let encoded: Data! do { - encoded = try ParseCoding.jsonEncoder().encode(response) + encoded = try ParseCoding.jsonEncoder().encode(fileResponse) } catch { XCTFail("Should encode/decode. Error \(error)") return @@ -1362,8 +1368,8 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var counter = 0 var savedFile: ParseFile? savedChildFiles.forEach { (_, value) in - XCTAssertEqual(value.url, response.url) - XCTAssertEqual(value.name, response.name) + XCTAssertEqual(value.url, fileResponse.url) + XCTAssertEqual(value.name, fileResponse.name) if counter == 0 { savedFile = value } diff --git a/Tests/ParseSwiftTests/ParseOperationTests.swift b/Tests/ParseSwiftTests/ParseOperationTests.swift index 9480b8d29..794b818ee 100644 --- a/Tests/ParseSwiftTests/ParseOperationTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationTests.swift @@ -82,7 +82,6 @@ class ParseOperationTests: XCTestCase { XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/classes/\(className)/\(objectId)") XCTAssertEqual(command.method, API.Method.PUT) - XCTAssertNil(command.params) guard let body = command.body else { XCTFail("Should be able to unwrap") diff --git a/Tests/ParseSwiftTests/ParseUserTests.swift b/Tests/ParseSwiftTests/ParseUserTests.swift index 45846b226..25e6c2367 100644 --- a/Tests/ParseSwiftTests/ParseUserTests.swift +++ b/Tests/ParseSwiftTests/ParseUserTests.swift @@ -754,13 +754,12 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length } #endif - func testSignupCommandWithBody() { + func testSignupCommandWithBody() throws { let body = SignupLoginBody(username: "test", password: "user") - let command = User.signupCommand(body: body) + let command = try User.signupCommand(body: body) XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/users") XCTAssertEqual(command.method, API.Method.POST) - XCTAssertNil(command.params) XCTAssertEqual(command.body?.username, body.username) XCTAssertEqual(command.body?.password, body.password) } @@ -868,7 +867,6 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/login") XCTAssertEqual(command.method, API.Method.POST) - XCTAssertNil(command.params) XCTAssertNotNil(command.body) } @@ -976,7 +974,6 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/logout") XCTAssertEqual(command.method, API.Method.POST) - XCTAssertNil(command.params) XCTAssertNil(command.body) } @@ -1064,7 +1061,6 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/requestPasswordReset") XCTAssertEqual(command.method, API.Method.POST) - XCTAssertNil(command.params) XCTAssertEqual(command.body?.email, body.email) } @@ -1177,7 +1173,6 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/verificationEmailRequest") XCTAssertEqual(command.method, API.Method.POST) - XCTAssertNil(command.params) XCTAssertEqual(command.body?.email, body.email) } @@ -1307,7 +1302,6 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/users/\(objectId)") XCTAssertEqual(command.method, API.Method.DELETE) - XCTAssertNil(command.params) XCTAssertNil(command.body) } catch { XCTFail(error.localizedDescription)