Skip to content

Add Roles and Relations #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c980339
WIP
cbaker6 Jan 17, 2021
1c2de0b
Add role
cbaker6 Jan 17, 2021
b176a2b
Add Operation tests, some renaming
cbaker6 Jan 18, 2021
4b3ae40
name change
cbaker6 Jan 18, 2021
29de21e
Add all operation tests and playgrounds example
cbaker6 Jan 18, 2021
1d9f97b
Add/Remove Relation operation
cbaker6 Jan 18, 2021
9b9b6a9
Merge remote-tracking branch 'refs/remotes/origin/main'
cbaker6 Jan 18, 2021
d29c2fe
merge and update
cbaker6 Jan 18, 2021
d448bae
Add Query "distinct"
cbaker6 Jan 18, 2021
6b0ebab
Make query thread safe since it's a reference type.
cbaker6 Jan 18, 2021
b72130e
remove old file
cbaker6 Jan 18, 2021
392d19e
more compatibility for linux build
cbaker6 Jan 18, 2021
f56712d
use new cache for jazzy
cbaker6 Jan 18, 2021
30a73c4
initial role
cbaker6 Jan 19, 2021
349321a
Clean up
cbaker6 Jan 19, 2021
551ad6e
Update naming conventions
cbaker6 Jan 19, 2021
e3ff368
Update ParseSwift initialization/
cbaker6 Jan 19, 2021
af915ab
Improved ParseACL
cbaker6 Jan 19, 2021
ec8c374
remove renamed files
cbaker6 Jan 19, 2021
b61c6b1
Working Role and Relation
cbaker6 Jan 20, 2021
9b5ea85
Make Query a value type instead of reference type
cbaker6 Jan 20, 2021
5b94df2
Remove ParseRelation.save, ParseOperations can handle all saves for R…
cbaker6 Jan 20, 2021
6a0c23d
More ParseRelation tests
cbaker6 Jan 20, 2021
b77ff06
Add ParseRole tests
cbaker6 Jan 20, 2021
813bca9
Added Relation query tests. Fixed some bugs in batch object test case…
cbaker6 Jan 21, 2021
8d3530c
More tests
cbaker6 Jan 21, 2021
68c747e
Add complete ParseRole tutorial to Playgrounds.
cbaker6 Jan 21, 2021
82cc137
Finished Playground examples.
cbaker6 Jan 21, 2021
6bb02bb
Add missing query constraints with tests.
cbaker6 Jan 21, 2021
b1cc093
Apply suggestions from code review
cbaker6 Jan 21, 2021
7956525
Merge remote-tracking branch 'refs/remotes/origin/roles'
cbaker6 Jan 21, 2021
ed6ec9d
Apply suggestions from code review
cbaker6 Jan 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ coverage:
status:
patch:
default:
target: 67
target: auto
changes: false
project:
default:
target: 74
target: 76
comment:
require_changes: true
9 changes: 2 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:
branches: '*'
env:
CI_XCODE_VER: '/Applications/Xcode_11.7.app/Contents/Developer'
CI_XCODE_VER_12: '/Applications/Xcode_12.2.app/Contents/Developer'

jobs:
xcode-test-ios:
Expand Down Expand Up @@ -98,19 +97,15 @@ jobs:
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
key: ${{ runner.os }}-gem-v1-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gem-
${{ runner.os }}-gem-v1
- name: Install Bundle
run: |
bundle config path vendor/bundle
bundle install
env:
DEVELOPER_DIR: ${{ env.CI_XCODE_VER_12 }}
- name: Create Jazzy Docs
run: ./Scripts/jazzy.sh
env:
DEVELOPER_DIR: ${{ env.CI_XCODE_VER_12 }}
- name: Deploy Jazzy Docs
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,15 @@ let score2ToFetch = GameScore(objectId: score2ForFetchedLater?.objectId)
}
}

var fetchedScore: GameScore!

//: Synchronously fetchAll GameScore's based on it's objectId's alone.
do {
let fetchedScores = try [scoreToFetch, score2ToFetch].fetchAll()
fetchedScores.forEach { result in
switch result {
case .success(let fetched):
fetchedScore = fetched
print("Successfully fetched: \(fetched)")
case .failure(let error):
print("Error fetching: \(error)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
//: [Previous](@previous)

import PlaygroundSupport
import Foundation
import ParseSwift

PlaygroundPage.current.needsIndefiniteExecution = true
initializeParse()

struct User: ParseUser {
//: These are required for ParseObject
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?

//: These are required for ParseUser
var username: String?
var email: String?
var password: String?
var authData: [String: [String: String]?]?

//: Your custom keys
var customKey: String?
}

struct Role<RoleUser: ParseUser>: ParseRole {

// required by ParseObject
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?

// provided by Role
var name: String

init() {
self.name = ""
}
}

//: Roles can provide additional access/security to your apps.

//: This variable will store the saved role
var savedRole: Role<User>?

//: Now we will create the Role.
if let currentUser = User.current {

//: Every Role requires an ACL that can't be changed after saving.
var acl = ParseACL()
acl.setReadAccess(user: currentUser, value: true)
acl.setWriteAccess(user: currentUser, value: true)

do {
//: Create the actual Role with a name and ACL.
var adminRole = try Role<User>(name: "Administrator", acl: acl)
adminRole.save { result in
switch result {
case .success(let saved):
print("The role saved successfully: \(saved)")
print("Check your \"Role\" class in Parse Dashboard.")

//: Store the saved role so we can use it later...
savedRole = saved

case .failure(let error):
print("Error saving role: \(error)")
}
}
} catch {
print("Error: \(error)")
}
}

//: Lets check to see if our Role has saved
if savedRole != nil {
print("We have a saved Role")
}

//: Users can be added to our previously saved Role.
do {
//: `ParseRoles` have `ParseRelations` that relate them either `ParseUser` and `ParseRole` objects.
//: The `ParseUser` relations can be accessed using `users`. We can then add `ParseUser`'s to the relation.
try savedRole!.users.add([User.current!]).save { result in
switch result {
case .success(let saved):
print("The role saved successfully: \(saved)")
print("Check \"users\" field in your \"Role\" class in Parse Dashboard.")

case .failure(let error):
print("Error saving role: \(error)")
}
}

} catch {
print("Error: \(error)")
}

//: To retrieve the users who are all Administrators, we need to query the relation.
let templateUser = User()
savedRole!.users.query(templateUser).find { result in
switch result {
case .success(let relatedUsers):
print("The following users are part of the \"\(savedRole!.name) role: \(relatedUsers)")

case .failure(let error):
print("Error saving role: \(error)")
}
}

//: Of course, you can remove users from the roles as well.
try savedRole!.users.remove([User.current!]).save { result in
switch result {
case .success(let saved):
print("The role removed successfully: \(saved)")
print("Check \"users\" field in your \"Role\" class in Parse Dashboard.")

case .failure(let error):
print("Error saving role: \(error)")
}
}

//: Additional roles can be created and tied to already created roles. Lets create a "Member" role.

//: This variable will store the saved role
var savedRoleModerator: Role<User>?

//: We need another ACL
var acl = ParseACL()
acl.setReadAccess(user: User.current!, value: true)
acl.setWriteAccess(user: User.current!, value: false)

do {
//: Create the actual Role with a name and ACL.
var memberRole = try Role<User>(name: "Member", acl: acl)
memberRole.save { result in
switch result {
case .success(let saved):
print("The role saved successfully: \(saved)")
print("Check your \"Role\" class in Parse Dashboard.")

//: Store the saved role so we can use it later...
savedRoleModerator = saved

case .failure(let error):
print("Error saving role: \(error)")
}
}
} catch {
print("Error: \(error)")
}

//: Lets check to see if our Role has saved
if savedRoleModerator != nil {
print("We have a saved Role")
}

//: Roles can be added to our previously saved Role.
do {
//: `ParseRoles` have `ParseRelations` that relate them either `ParseUser` and `ParseRole` objects.
//: The `ParseUser` relations can be accessed using `users`. We can then add `ParseUser`'s to the relation.
try savedRole!.roles.add([savedRoleModerator!]).save { result in
switch result {
case .success(let saved):
print("The role saved successfully: \(saved)")
print("Check \"roles\" field in your \"Role\" class in Parse Dashboard.")

case .failure(let error):
print("Error saving role: \(error)")
}
}

} catch {
print("Error: \(error)")
}

//: To retrieve the users who are all Administrators, we need to query the relation.
//: This time we will use a helper query from `ParseRole`.
savedRole!.queryRoles?.find { result in
switch result {
case .success(let relatedRoles):
print("The following roles are part of the \"\(savedRole!.name) role: \(relatedRoles)")

case .failure(let error):
print("Error saving role: \(error)")
}
}

//: Of course, you can remove users from the roles as well.
try savedRole!.roles.remove([savedRoleModerator!]).save { result in
switch result {
case .success(let saved):
print("The role removed successfully: \(saved)")
print("Check the \"roles\" field in your \"Role\" class in Parse Dashboard.")

case .failure(let error):
print("Error saving role: \(error)")
}
}

//: All `ParseObjects` have a `ParseRelation` attribute that be used on instances.
//: For example, the User has:
let relation = User.current!.relation

//: Example: relation.add(<#T##users: [ParseUser]##[ParseUser]#>)
//: Example: relation.remove(<#T##key: String##String#>, objects: <#T##[ParseObject]#>)

//: Using this relation, you can create many-to-many relationships with other `ParseObjecs`,
//: similar to `users` and `roles`.

PlaygroundPage.current.finishExecution()

//: [Next](@next)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//: [Previous](@previous)

import PlaygroundSupport
import Foundation
import ParseSwift

PlaygroundPage.current.needsIndefiniteExecution = true
initializeParse()

//: Some ValueTypes ParseObject's we will use...
struct GameScore: ParseObject {
//: Those are required for Object
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?

//: Your own properties
var score: Int = 0

//custom initializer
init(score: Int) {
self.score = score
}

init(objectId: String?) {
self.objectId = objectId
}
}

//: You can have the server do operations on your ParseObjects for you.

//: First lets create another GameScore
let savedScore: GameScore!
do {
savedScore = try GameScore(score: 102).save()
} catch {
savedScore = nil
fatalError("Error saving: \(error)")
}

//: Then we will increment the score.
let incrementOperation = savedScore
.operation.increment("score", by: 1)

incrementOperation.save { result in
switch result {
case .success:
print("Original score: \(savedScore). Check the new score on Parse Dashboard.")
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}

//: You can increment the score again syncronously.
do {
_ = try incrementOperation.save()
print("Original score: \(savedScore). Check the new score on Parse Dashboard.")
} catch {
print(error)
}

//: There are other operations: add/remove/delete objects from `ParseObjects`.
//: In fact, the `users` and `roles` relations from `ParseRoles` used the add/remove operations.
let operations = savedScore.operation

//: Example: operations.add("hello", objects: ["test"])

PlaygroundPage.current.finishExecution()
//: [Next](@next)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ score.score = 200
try score.save()

let afterDate = Date().addingTimeInterval(-300)
let query = GameScore.query("score" > 100, "createdAt" > afterDate)
var query = GameScore.query("score" > 100, "createdAt" > afterDate)

// Query asynchronously (preferred way) - Performs work on background
// queue and returns to designated on designated callbackQueue.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ struct User: ParseUser {
var updatedAt: Date?
var ACL: ParseACL?

// These are required for ParseUser
//: These are required for ParseUser
var username: String?
var email: String?
var password: String?
var authData: [String: [String: String]?]?

//: Your custom keys
var customKey: String?
Expand Down
2 changes: 2 additions & 0 deletions ParseSwift.playground/contents.xcplayground
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
<page name='9 - Files'/>
<page name='10 - Cloud Code'/>
<page name='11 - LiveQuery'/>
<page name='12 - Roles and Relations'/>
<page name='13 - Operations'/>
</pages>
</playground>
Loading