From 51e16d0764bc5f65b0b78b982b2c4febff421818 Mon Sep 17 00:00:00 2001 From: Corey Date: Sat, 29 Jan 2022 18:49:29 -0500 Subject: [PATCH] fix: allow ParseRole to save when using custom objectId's (#338) * fix: allow ParseRole to save when using custom objectId's * codecov --- .codecov.yml | 2 +- CHANGELOG.md | 8 +- Sources/ParseSwift/ParseConstants.swift | 2 +- Sources/ParseSwift/Types/ParseOperation.swift | 4 +- .../ParseSwiftTests/ParseRelationTests.swift | 6 +- Tests/ParseSwiftTests/ParseRoleTests.swift | 312 ++++++++++++++++++ 6 files changed, 326 insertions(+), 8 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index a26c023fb..728915380 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,7 +6,7 @@ coverage: status: patch: default: - target: 78 + target: auto changes: false project: default: diff --git a/CHANGELOG.md b/CHANGELOG.md index c1c3f7ab7..dae8d7d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.0.0...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.0.1...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 4.0.1 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.0.0...4.0.1) + +__Fixes__ +- Allow ParseRole's to be updated when the SDK is allowing custom objectId's ([#338](https://github.com/parse-community/Parse-Swift/pull/338)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 4.0.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/3.1.2...4.0.0) diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 4f8da54fb..722acb452 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "4.0.0" + static let version = "4.0.1" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Types/ParseOperation.swift b/Sources/ParseSwift/Types/ParseOperation.swift index f4a6af2ac..5b55d3342 100644 --- a/Sources/ParseSwift/Types/ParseOperation.swift +++ b/Sources/ParseSwift/Types/ParseOperation.swift @@ -386,7 +386,7 @@ extension ParseOperation { - returns: Returns saved `ParseObject`. */ public func save(options: API.Options = []) throws -> T { - if !target.isSaved { + guard target.objectId != nil else { throw ParseError(code: .missingObjectId, message: "ParseObject isn't saved.") } return try saveCommand() @@ -406,7 +406,7 @@ extension ParseOperation { callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void ) { - if !target.isSaved { + guard target.objectId != nil else { callbackQueue.async { let error = ParseError(code: .missingObjectId, message: "ParseObject isn't saved.") completion(.failure(error)) diff --git a/Tests/ParseSwiftTests/ParseRelationTests.swift b/Tests/ParseSwiftTests/ParseRelationTests.swift index d2a92b549..3b82a86e3 100644 --- a/Tests/ParseSwiftTests/ParseRelationTests.swift +++ b/Tests/ParseSwiftTests/ParseRelationTests.swift @@ -215,7 +215,7 @@ class ParseRelationTests: XCTestCase { XCTAssertThrowsError(try relation.add("level", objects: [level])) } - func testAddOperations() throws { + func testAddOperation() throws { var score = GameScore(points: 10) let objectId = "hello" score.objectId = objectId @@ -252,7 +252,7 @@ class ParseRelationTests: XCTestCase { XCTAssertThrowsError(try relation.add("yolo", objects: [level])) } - func testAddOperationsNoKey() throws { + func testAddOperationNoKey() throws { var score = GameScore(points: 10) let objectId = "hello" score.objectId = objectId @@ -274,7 +274,7 @@ class ParseRelationTests: XCTestCase { XCTAssertEqual(decoded, expected) } - func testAddOperationsKeyCheck() throws { + func testAddOperationKeyCheck() throws { var score = GameScore(points: 10) let objectId = "hello" score.objectId = objectId diff --git a/Tests/ParseSwiftTests/ParseRoleTests.swift b/Tests/ParseSwiftTests/ParseRoleTests.swift index 539ce58de..bfe43142e 100644 --- a/Tests/ParseSwiftTests/ParseRoleTests.swift +++ b/Tests/ParseSwiftTests/ParseRoleTests.swift @@ -377,6 +377,318 @@ class ParseRoleTests: XCTestCase { XCTAssertEqual(decoded2, expected2) } + func testRoleAddOperationSaveSynchronous() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + let operation = try roles.add([newRole]) + + var serverResponse = role + serverResponse.createdAt = nil + serverResponse.updatedAt = Date() + + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + //Get dates in correct format from ParseDecoding strategy + serverResponse = try serverResponse.getDecoder().decode(Role.self, from: encoded) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let updatedRole = try operation.save() + XCTAssertEqual(updatedRole.updatedAt, serverResponse.updatedAt) + XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse)) + } + + func testRoleAddOperationSaveSynchronousError() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + var operation = try roles.add([newRole]) + operation.target.objectId = nil + + do { + _ = try operation.save() + XCTFail("Should have failed") + } catch { + XCTAssertTrue(error.containedIn([.missingObjectId])) + } + } + + func testRoleAddOperationSaveSynchronousCustomObjectId() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + ParseSwift.configuration.isAllowingCustomObjectIds = true + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + let operation = try roles.add([newRole]) + + var serverResponse = role + serverResponse.createdAt = nil + serverResponse.updatedAt = Date() + + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + //Get dates in correct format from ParseDecoding strategy + serverResponse = try serverResponse.getDecoder().decode(Role.self, from: encoded) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let updatedRole = try operation.save() + XCTAssertEqual(updatedRole.updatedAt, serverResponse.updatedAt) + XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse)) + } + + func testRoleAddOperationSaveSynchronousCustomObjectIdError() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + ParseSwift.configuration.isAllowingCustomObjectIds = true + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + var operation = try roles.add([newRole]) + operation.target.objectId = nil + + do { + _ = try operation.save() + XCTFail("Should have failed") + } catch { + XCTAssertTrue(error.containedIn([.missingObjectId])) + } + } + + func testRoleAddOperationSaveAsynchronous() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + let operation = try roles.add([newRole]) + + var serverResponse = role + serverResponse.createdAt = nil + serverResponse.updatedAt = Date() + + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + //Get dates in correct format from ParseDecoding strategy + serverResponse = try serverResponse.getDecoder().decode(Role.self, from: encoded) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let expectation1 = XCTestExpectation(description: "Save object1") + operation.save { result in + switch result { + case .success(let updatedRole): + XCTAssertEqual(updatedRole.updatedAt, serverResponse.updatedAt) + XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse)) + case .failure(let error): + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + } + wait(for: [expectation1], timeout: 20.0) + } + + func testRoleAddOperationSaveAsynchronousError() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + ParseSwift.configuration.isAllowingCustomObjectIds = true + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + var operation = try roles.add([newRole]) + operation.target.objectId = nil + + let expectation1 = XCTestExpectation(description: "Save object1") + operation.save { result in + switch result { + case .success: + XCTFail("Should have failed") + case .failure(let error): + XCTAssertEqual(error.code, .missingObjectId) + } + expectation1.fulfill() + } + wait(for: [expectation1], timeout: 20.0) + } + + func testRoleAddOperationSaveAsynchronousCustomObjectId() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + ParseSwift.configuration.isAllowingCustomObjectIds = true + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + let operation = try roles.add([newRole]) + + var serverResponse = role + serverResponse.createdAt = nil + serverResponse.updatedAt = Date() + + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + //Get dates in correct format from ParseDecoding strategy + serverResponse = try serverResponse.getDecoder().decode(Role.self, from: encoded) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let expectation1 = XCTestExpectation(description: "Save object1") + operation.save { result in + switch result { + case .success(let updatedRole): + XCTAssertEqual(updatedRole.updatedAt, serverResponse.updatedAt) + XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse)) + case .failure(let error): + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + } + wait(for: [expectation1], timeout: 20.0) + } + + func testRoleAddOperationSaveAsynchronousCustomObjectIdError() throws { + var acl = ParseACL() + acl.publicWrite = false + acl.publicRead = true + + var role = try Role(name: "Administrator", acl: acl) + role.createdAt = Date() + role.updatedAt = Date() + XCTAssertNil(role.roles) // Shouldn't produce a relation without an objectId. + role.objectId = "yolo" + guard let roles = role.roles else { + XCTFail("Should have unwrapped") + return + } + + var newRole = try Role(name: "Moderator", acl: acl) + newRole.objectId = "heel" + var operation = try roles.add([newRole]) + operation.target.objectId = nil + + let expectation1 = XCTestExpectation(description: "Save object1") + operation.save { result in + switch result { + case .success: + XCTFail("Should have failed") + case .failure(let error): + XCTAssertEqual(error.code, .missingObjectId) + } + expectation1.fulfill() + } + wait(for: [expectation1], timeout: 20.0) + } + func testRoleAddOperationNoKey() throws { var acl = ParseACL() acl.publicWrite = false