Skip to content

Commit 80802c5

Browse files
authored
Add Idempotency (#62)
* Add Idempodency * restore playground * new versions of swiftlint complains about playground comments * Update CHANGELOG.md
1 parent 1628614 commit 80802c5

File tree

11 files changed

+55
-33
lines changed

11 files changed

+55
-33
lines changed

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ disabled_rules:
44
- function_body_length
55
- type_body_length
66
- inclusive_language
7+
- comment_spacing

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.0.2...main)
55
* _Contributing to this repo? Add info about your change here to be included in next release_
66

7+
__New features__
8+
- Idempotency support ([#62](https://github.com/parse-community/Parse-Swift/pull/62)), thanks to [Corey Baker](https://github.com/cbaker6).
9+
710
### 1.0.2
811
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.0.0...1.0.2)
912

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

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ if let currentUser = User.current {
5555

5656
do {
5757
//: Create the actual Role with a name and ACL.
58-
var adminRole = try Role<User>(name: "Administrator", acl: acl)
58+
let adminRole = try Role<User>(name: "Administrator", acl: acl)
5959
adminRole.save { result in
6060
switch result {
6161
case .success(let saved):
@@ -100,26 +100,34 @@ do {
100100

101101
//: To retrieve the users who are all Administrators, we need to query the relation.
102102
let templateUser = User()
103-
savedRole!.users.query(templateUser).find { result in
104-
switch result {
105-
case .success(let relatedUsers):
106-
print("The following users are part of the \"\(savedRole!.name) role: \(relatedUsers)")
103+
do {
104+
try savedRole!.users.query(templateUser).find { result in
105+
switch result {
106+
case .success(let relatedUsers):
107+
print("The following users are part of the \"\(savedRole!.name) role: \(relatedUsers)")
107108

108-
case .failure(let error):
109-
print("Error saving role: \(error)")
109+
case .failure(let error):
110+
print("Error saving role: \(error)")
111+
}
110112
}
113+
} catch {
114+
print(error)
111115
}
112116

113117
//: Of course, you can remove users from the roles as well.
114-
try savedRole!.users.remove([User.current!]).save { result in
115-
switch result {
116-
case .success(let saved):
117-
print("The role removed successfully: \(saved)")
118-
print("Check \"users\" field in your \"Role\" class in Parse Dashboard.")
118+
do {
119+
try savedRole!.users.remove([User.current!]).save { result in
120+
switch result {
121+
case .success(let saved):
122+
print("The role removed successfully: \(saved)")
123+
print("Check \"users\" field in your \"Role\" class in Parse Dashboard.")
119124

120-
case .failure(let error):
121-
print("Error saving role: \(error)")
125+
case .failure(let error):
126+
print("Error saving role: \(error)")
127+
}
122128
}
129+
} catch {
130+
print(error)
123131
}
124132

125133
//: Additional roles can be created and tied to already created roles. Lets create a "Member" role.
@@ -134,7 +142,7 @@ acl.setWriteAccess(user: User.current!, value: false)
134142

135143
do {
136144
//: Create the actual Role with a name and ACL.
137-
var memberRole = try Role<User>(name: "Member", acl: acl)
145+
let memberRole = try Role<User>(name: "Member", acl: acl)
138146
memberRole.save { result in
139147
switch result {
140148
case .success(let saved):
@@ -171,7 +179,6 @@ do {
171179
print("Error saving role: \(error)")
172180
}
173181
}
174-
175182
} catch {
176183
print("Error: \(error)")
177184
}
@@ -189,15 +196,19 @@ savedRole!.queryRoles?.find { result in
189196
}
190197

191198
//: Of course, you can remove users from the roles as well.
192-
try savedRole!.roles.remove([savedRoleModerator!]).save { result in
193-
switch result {
194-
case .success(let saved):
195-
print("The role removed successfully: \(saved)")
196-
print("Check the \"roles\" field in your \"Role\" class in Parse Dashboard.")
199+
do {
200+
try savedRole!.roles.remove([savedRoleModerator!]).save { result in
201+
switch result {
202+
case .success(let saved):
203+
print("The role removed successfully: \(saved)")
204+
print("Check the \"roles\" field in your \"Role\" class in Parse Dashboard.")
197205

198-
case .failure(let error):
199-
print("Error saving role: \(error)")
206+
case .failure(let error):
207+
print("Error saving role: \(error)")
208+
}
200209
}
210+
} catch {
211+
print(error)
201212
}
202213

203214
//: All `ParseObjects` have a `ParseRelation` attribute that be used on instances.

ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ let incrementOperation = savedScore
4545
incrementOperation.save { result in
4646
switch result {
4747
case .success:
48-
print("Original score: \(savedScore). Check the new score on Parse Dashboard.")
48+
print("Original score: \(String(describing: savedScore)). Check the new score on Parse Dashboard.")
4949
case .failure(let error):
5050
assertionFailure("Error saving: \(error)")
5151
}
@@ -54,7 +54,7 @@ incrementOperation.save { result in
5454
//: You can increment the score again syncronously.
5555
do {
5656
_ = try incrementOperation.save()
57-
print("Original score: \(savedScore). Check the new score on Parse Dashboard.")
57+
print("Original score: \(String(describing: savedScore)). Check the new score on Parse Dashboard.")
5858
} catch {
5959
print(error)
6060
}

ParseSwift.playground/Pages/14 - Config.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ config.fetch { result in
6060
}
6161

6262
//: Anytime you fetch or update your Config successfully, it's automatically saved to your Keychain.
63-
print(Config.current)
63+
print(Config.current ?? "No config")
6464

6565
PlaygroundPage.current.finishExecution()
6666

ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ query.first { results in
5858
switch results {
5959
case .success(let score):
6060

61-
guard let objectId = score.objectId,
61+
guard score.objectId != nil,
6262
let createdAt = score.createdAt else { fatalError() }
6363
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
6464
print("Found score: \(score)")

ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ do {
8181
User.anonymous.login { result in
8282
switch result {
8383
case .success:
84-
print("Successfully logged in \(User.current)")
84+
print("Successfully logged in \(String(describing: User.current))")
8585
case .failure(let error):
8686
print("Error logging in: \(error)")
8787
}

ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ score.save { result in
7474
picture.fetch { result in
7575
switch result {
7676
case .success(let fetchedFile):
77-
print("The file is now saved on your device at: \(fetchedFile.localURL)")
77+
print("The file is now saved on your device at: \(String(describing: fetchedFile.localURL))")
7878
print("The full details of your profilePicture ParseFile are: \(fetchedFile)")
7979
case .failure(let error):
8080
assertionFailure("Error fetching: \(error)")
@@ -107,7 +107,7 @@ do {
107107

108108
//: To get the contents updated `ParseFile`, you need to fetch your GameScore.
109109
let fetchedScore = try savedScore.fetch()
110-
if var myData = fetchedScore.myData {
110+
if let myData = fetchedScore.myData {
111111

112112
guard let url = myData.url else {
113113
fatalError("Error: file should have url.")

ParseSwift.playground/contents.xcplayground

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
<page name='8 - Pointers'/>
1212
<page name='9 - Files'/>
1313
<page name='10 - Cloud Code'/>
14-
<page name='11 - LiveQuery'/>
15-
<page name='12 - Roles and Relations'/>
16-
<page name='13 - Operations'/>
14+
<page name='10 - Cloud Code'/>
15+
<page name='11 - LiveQuery'/>
16+
<page name='12 - Roles and Relations'/>
17+
<page name='13 - Operations'/>
1718
<page name='14 - Config'/>
1819
</pages>
1920
</playground>

Sources/ParseSwift/API/API.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ public struct API {
156156
headers["X-Parse-Installation-Id"] = installationId
157157
}
158158

159+
headers["X-Parse-Request-Id"] = UUID().uuidString.lowercased()
160+
159161
options.forEach { (option) in
160162
switch option {
161163
case .useMasterKey:

Tests/ParseSwiftTests/APICommandTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,8 @@ class APICommandTests: XCTestCase {
151151
}
152152
}
153153

154+
func testIdempodency() {
155+
let headers = API.getHeaders(options: [])
156+
XCTAssertNotNil(headers["X-Parse-Request-Id"])
157+
}
154158
}

0 commit comments

Comments
 (0)