Skip to content

Commit cc49b59

Browse files
authored
fix: restore name after updating ParseRole (#10)
* fix: restore name after updating ParseRole * nit
1 parent b96f4d7 commit cc49b59

File tree

5 files changed

+165
-17
lines changed

5 files changed

+165
-17
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
__New features__
88
- Added the ability to check if a `ParseObject` key is dirty ([#9](https://github.com/netreconlab/Parse-Swift/pull/9)), thanks to [Corey Baker](https://github.com/cbaker6).
99

10+
__Fixes__
11+
- Fixed an issue where the name propery of a ParseRole may not be restored after updating a ParseRole on the server ([#10](https://github.com/netreconlab/Parse-Swift/pull/10)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
1013
### 4.15.2
1114
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/4.15.1...4.15.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/4.15.2/documentation/parseswift)
1215

ParseSwift.playground/Pages/12 - Roles and Relations.xcplaygroundpage/Contents.swift

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,18 @@ struct Role<RoleUser: ParseUser>: ParseRole {
5858
//: Provided by Role.
5959
var name: String?
6060

61+
//: Custom properties.
62+
var subtitle: String
63+
6164
/*:
6265
Optional - implement your own version of merge
6366
for faster decoding after updating your `ParseObject`.
6467
*/
6568
func merge(with object: Self) throws -> Self {
6669
var updated = try mergeParse(with: object)
67-
if updated.shouldRestoreKey(\.name,
70+
if updated.shouldRestoreKey(\.subtitle,
6871
original: object) {
69-
updated.name = object.name
72+
updated.subtitle = object.subtitle
7073
}
7174
return updated
7275
}
@@ -124,7 +127,8 @@ acl.setWriteAccess(user: currentUser, value: true)
124127

125128
do {
126129
//: Create the actual Role with a name and ACL.
127-
let adminRole = try Role<User>(name: "Administrator", acl: acl)
130+
var adminRole = try Role<User>(name: "Administrator", acl: acl)
131+
adminRole.subtitle = "staff"
128132
adminRole.save { result in
129133
switch result {
130134
case .success(let saved):
@@ -166,7 +170,10 @@ do {
166170
print("Error: \(error)")
167171
}
168172

169-
//: To retrieve the users who are all Administrators, we need to query the relation.
173+
/*:
174+
To retrieve the users who are all Administrators,
175+
we need to query the relation.
176+
*/
170177
do {
171178
let query: Query<User>? = try savedRole!.users?.query()
172179
query?.find { result in
@@ -201,8 +208,10 @@ do {
201208
print(error)
202209
}
203210

204-
//: Additional roles can be created and tied to already created roles. Lets create a "Member" role.
205-
211+
/*:
212+
Additional roles can be created and tied to already created roles.
213+
Lets create a "Member" role.
214+
*/
206215
//: This variable will store the saved role.
207216
var savedRoleModerator: Role<User>?
208217

@@ -233,8 +242,12 @@ if savedRoleModerator != nil {
233242

234243
//: Roles can be added to our previously saved Role.
235244
do {
236-
//: `ParseRoles` have `ParseRelations` that relate them either `ParseUser` and `ParseRole` objects.
237-
//: The `ParseUser` relations can be accessed using `users`. We can then add `ParseUser`'s to the relation.
245+
/*:
246+
`ParseRoles` have `ParseRelations` that relate them either
247+
`ParseUser` and `ParseRole` objects. The `ParseUser`
248+
relations can be accessed using `users`. We can then add
249+
`ParseUser`'s to the relation.
250+
*/
238251
try savedRole!.roles?.add([savedRoleModerator!]).save { result in
239252
switch result {
240253
case .success(let saved):
@@ -249,8 +262,11 @@ do {
249262
print("Error: \(error)")
250263
}
251264

252-
//: To retrieve the users who are all Administrators, we need to query the relation.
253-
//: This time we will use a helper query from `ParseRole`.
265+
/*:
266+
To retrieve the users who are all Administrators,
267+
we need to query the relation. This time we will
268+
use a helper query from `ParseRole`.
269+
*/
254270
do {
255271
try savedRole!.queryRoles().find { result in
256272
switch result {
@@ -284,10 +300,12 @@ do {
284300
print(error)
285301
}
286302

287-
//: Using this relation, you can create one-to-many relationships with other `ParseObjecs`,
288-
//: similar to `users` and `roles`.
289-
//: All `ParseObject`s have a `ParseRelation` attribute that be used on instances.
290-
//: For example, the User has:
303+
/*:
304+
Using this relation, you can create one-to-many relationships
305+
with other `ParseObjecs`, similar to `users` and `roles`. All
306+
`ParseObject`s have a `ParseRelation` attribute that be used on
307+
instances. For example, the User has:
308+
*/
291309
var relation = User.current!.relation
292310
let score1 = GameScore(points: 53)
293311
let score2 = GameScore(points: 57)

ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ extension GameScore {
6060
//: Define initial GameScores.
6161
var score = GameScore(points: 40)
6262

63-
/*: Save asynchronously (preferred way) - Performs work on background
64-
queue and returns to specified callbackQueue.
65-
If no callbackQueue is specified it returns to main queue.
63+
/*:
64+
Save asynchronously (preferred way) - Performs work on background
65+
queue and returns to specified callbackQueue. If no callbackQueue
66+
is specified it returns to main queue.
6667
*/
6768
score.save { result in
6869
switch result {

Sources/ParseSwift/Objects/ParseRole.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,31 @@ public extension ParseRole {
101101
let name = self.name ?? self.objectId
102102
hasher.combine(name)
103103
}
104+
105+
func mergeParse(with object: Self) throws -> Self {
106+
guard hasSameObjectId(as: object) else {
107+
throw ParseError(code: .unknownError,
108+
message: "objectId's of objects do not match")
109+
}
110+
var updatedRole = self
111+
if shouldRestoreKey(\.ACL,
112+
original: object) {
113+
updatedRole.ACL = object.ACL
114+
}
115+
if shouldRestoreKey(\.name,
116+
original: object) {
117+
updatedRole.name = object.name
118+
}
119+
return updatedRole
120+
}
121+
122+
func merge(with object: Self) throws -> Self {
123+
do {
124+
return try mergeAutomatically(object)
125+
} catch {
126+
return try mergeParse(with: object)
127+
}
128+
}
104129
}
105130

106131
// MARK: Convenience

Tests/ParseSwiftTests/ParseRoleTests.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ class ParseRoleTests: XCTestCase {
6565

6666
// provided by Role
6767
var name: String?
68+
69+
// custom
70+
var title: String?
71+
72+
func merge(with object: Self) throws -> Self {
73+
var updated = try mergeParse(with: object)
74+
if updated.shouldRestoreKey(\.title,
75+
original: object) {
76+
updated.title = object.title
77+
}
78+
return updated
79+
}
6880
}
6981

7082
struct Level: ParseObject {
@@ -131,6 +143,49 @@ class ParseRoleTests: XCTestCase {
131143
XCTAssertEqual(role.endpoint.urlComponent, "/roles/me")
132144
}
133145

146+
func testSaveUpdateCommandParseObjectMutable() throws {
147+
var role = try Role<User>(name: "Administrator")
148+
let className = role.className
149+
let objectId = "yarr"
150+
role.objectId = objectId
151+
role.createdAt = Date()
152+
role.updatedAt = role.createdAt
153+
154+
let command = try role.mergeable.saveCommand()
155+
XCTAssertNotNil(command)
156+
XCTAssertEqual(command.path.urlComponent, "/classes/\(className)/\(objectId)")
157+
XCTAssertEqual(command.method, API.Method.PUT)
158+
XCTAssertNil(command.params)
159+
160+
guard let body = command.body else {
161+
XCTFail("Should be able to unwrap")
162+
return
163+
}
164+
165+
let expected = "{}"
166+
let encoded = try ParseCoding.parseEncoder()
167+
.encode(body, collectChildren: false,
168+
objectsSavedBeforeThisOne: nil,
169+
filesSavedBeforeThisOne: nil).encoded
170+
let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8))
171+
XCTAssertEqual(decoded, expected)
172+
173+
var empty = role.mergeable
174+
empty.title = "hello"
175+
let command2 = try empty.saveCommand()
176+
guard let body2 = command2.body else {
177+
XCTFail("Should be able to unwrap")
178+
return
179+
}
180+
let expected2 = "{\"title\":\"hello\"}"
181+
let encoded2 = try ParseCoding.parseEncoder()
182+
.encode(body2, collectChildren: false,
183+
objectsSavedBeforeThisOne: nil,
184+
filesSavedBeforeThisOne: nil).encoded
185+
let decoded2 = try XCTUnwrap(String(data: encoded2, encoding: .utf8))
186+
XCTAssertEqual(decoded2, expected2)
187+
}
188+
134189
func testUserAddIncorrectClassKeyError() throws {
135190
var acl = ParseACL()
136191
acl.publicWrite = false
@@ -419,6 +474,52 @@ class ParseRoleTests: XCTestCase {
419474
XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse))
420475
}
421476

477+
func testRoleUpdateMergeSynchronous() throws {
478+
var acl = ParseACL()
479+
acl.publicWrite = false
480+
acl.publicRead = true
481+
482+
var role = try Role<User>(name: "Administrator", acl: acl)
483+
role.createdAt = Date()
484+
role.updatedAt = Date()
485+
role.title = "hello"
486+
XCTAssertNil(role.roles) // Should not produce a relation without an objectId.
487+
role.objectId = "yolo"
488+
guard role.roles != nil else {
489+
XCTFail("Should have unwrapped")
490+
return
491+
}
492+
493+
var newRole = try Role<User>(name: "Moderator", acl: acl)
494+
newRole.objectId = "heel"
495+
496+
var serverResponse = role
497+
serverResponse.createdAt = nil
498+
serverResponse.updatedAt = Date()
499+
500+
let encoded: Data!
501+
do {
502+
encoded = try ParseCoding.jsonEncoder().encode(serverResponse)
503+
//Get dates in correct format from ParseDecoding strategy
504+
serverResponse = try serverResponse.getDecoder().decode(Role<User>.self, from: encoded)
505+
} catch {
506+
XCTFail("Should encode/decode. Error \(error)")
507+
return
508+
}
509+
510+
MockURLProtocol.mockRequests { _ in
511+
return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0)
512+
}
513+
514+
var changedRole = role
515+
.set(\.title, to: "peace")
516+
let updatedRole = try changedRole.save()
517+
XCTAssertEqual(updatedRole.updatedAt, serverResponse.updatedAt)
518+
XCTAssertEqual(updatedRole.name, serverResponse.name)
519+
XCTAssertEqual(updatedRole.title, changedRole.title)
520+
XCTAssertTrue(updatedRole.hasSameObjectId(as: serverResponse))
521+
}
522+
422523
func testRoleAddOperationSaveSynchronousError() throws {
423524
var acl = ParseACL()
424525
acl.publicWrite = false

0 commit comments

Comments
 (0)