From 56ed0a4990cace6509a4eefdae23076bfde6a877 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 21 May 2022 17:02:29 -0400 Subject: [PATCH 01/55] wip --- ParseSwift.xcodeproj/project.pbxproj | 30 ++ Sources/ParseSwift/API/API.swift | 15 + Sources/ParseSwift/Objects/ParseUser.swift | 2 +- Sources/ParseSwift/Types/ParseField.swift | 73 ++++ .../ParseSwift/Types/ParseSchema+async.swift | 30 ++ Sources/ParseSwift/Types/ParseSchema.swift | 361 ++++++++++++++++++ 6 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 Sources/ParseSwift/Types/ParseField.swift create mode 100644 Sources/ParseSwift/Types/ParseSchema+async.swift create mode 100644 Sources/ParseSwift/Types/ParseSchema.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 55ff7dbba..5c56024c2 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -342,6 +342,18 @@ 708D035325215F9B00646C70 /* Deletable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708D035125215F9B00646C70 /* Deletable.swift */; }; 708D035425215F9B00646C70 /* Deletable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708D035125215F9B00646C70 /* Deletable.swift */; }; 708D035525215F9B00646C70 /* Deletable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708D035125215F9B00646C70 /* Deletable.swift */; }; + 709A147D283949D100BF85E5 /* ParseSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A147C283949D100BF85E5 /* ParseSchema.swift */; }; + 709A147E283949D100BF85E5 /* ParseSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A147C283949D100BF85E5 /* ParseSchema.swift */; }; + 709A147F283949D100BF85E5 /* ParseSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A147C283949D100BF85E5 /* ParseSchema.swift */; }; + 709A1480283949D100BF85E5 /* ParseSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A147C283949D100BF85E5 /* ParseSchema.swift */; }; + 709A148228395ED100BF85E5 /* ParseSchema+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148128395ED100BF85E5 /* ParseSchema+async.swift */; }; + 709A148328395ED100BF85E5 /* ParseSchema+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148128395ED100BF85E5 /* ParseSchema+async.swift */; }; + 709A148428395ED100BF85E5 /* ParseSchema+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148128395ED100BF85E5 /* ParseSchema+async.swift */; }; + 709A148528395ED100BF85E5 /* ParseSchema+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148128395ED100BF85E5 /* ParseSchema+async.swift */; }; + 709A148728396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; + 709A148828396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; + 709A148928396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; + 709A148A28396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -968,6 +980,9 @@ 7085DDA226CC8A470033B977 /* ParseHealth+combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ParseHealth+combine.swift"; sourceTree = ""; }; 7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAuthenticationCombineTests.swift; sourceTree = ""; }; 708D035125215F9B00646C70 /* Deletable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deletable.swift; sourceTree = ""; }; + 709A147C283949D100BF85E5 /* ParseSchema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchema.swift; sourceTree = ""; }; + 709A148128395ED100BF85E5 /* ParseSchema+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+async.swift"; sourceTree = ""; }; + 709A148628396B1C00BF85E5 /* ParseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseField.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1675,6 +1690,7 @@ 703B090B26BD984D005A112F /* ParseConfig+async.swift */, 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */, F97B45BF24D9C6F200F4A88B /* ParseError.swift */, + 709A148628396B1C00BF85E5 /* ParseField.swift */, F97B45C124D9C6F200F4A88B /* ParseFile.swift */, 7045769C26BD934000F86F71 /* ParseFile+async.swift */, 7044C19025C4F5B60011F6E7 /* ParseFile+combine.swift */, @@ -1687,6 +1703,8 @@ 7044C19E25C4FA870011F6E7 /* ParseOperation+combine.swift */, 91285B1B26990D7F0051B544 /* ParsePolygon.swift */, 7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */, + 709A147C283949D100BF85E5 /* ParseSchema.swift */, + 709A148128395ED100BF85E5 /* ParseSchema+async.swift */, 91679D63268E596300F71809 /* ParseVersion.swift */, F97B45BE24D9C6F200F4A88B /* Pointer.swift */, 70C167B327304F09009F4E30 /* Pointer+async.swift */, @@ -2225,6 +2243,7 @@ 700395A325A119430052CB31 /* Operations.swift in Sources */, 91BB8FCF2690BA70005A6BA5 /* QueryObservable.swift in Sources */, 70F03A232780BDE200E5AFB4 /* ParseGoogle.swift in Sources */, + 709A148228395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769826BD917500F86F71 /* Query+async.swift in Sources */, 703B094E26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3825D998D90048EC1B /* ParseLDAP.swift in Sources */, @@ -2263,10 +2282,12 @@ F97B45E224D9C6F200F4A88B /* AnyEncodable.swift in Sources */, 700396EA25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F66F26A35D610082F6D6 /* URLCache.swift in Sources */, + 709A148728396B1D00BF85E5 /* ParseField.swift in Sources */, 70572671259033A700F0ADD5 /* ParseFileManager.swift in Sources */, 70F03A342780CA4300E5AFB4 /* ParseGitHub.swift in Sources */, 707A3C2025B14BD0000D215C /* ParseApple.swift in Sources */, 703B095D26BF481F005A112F /* ParseFacebook+async.swift in Sources */, + 709A147D283949D100BF85E5 /* ParseSchema.swift in Sources */, F97B462224D9C6F200F4A88B /* ParseKeyValueStore.swift in Sources */, 703B090226BD9652005A112F /* ParseAnalytics+async.swift in Sources */, 703B093F26BF47AC005A112F /* ParseApple+async.swift in Sources */, @@ -2459,6 +2480,7 @@ 700395A425A119430052CB31 /* Operations.swift in Sources */, 91BB8FD02690BA70005A6BA5 /* QueryObservable.swift in Sources */, 7045769926BD917500F86F71 /* Query+async.swift in Sources */, + 709A148328395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 703B094F26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3925D998D90048EC1B /* ParseLDAP.swift in Sources */, 700395F325A171320052CB31 /* LiveQueryable.swift in Sources */, @@ -2497,10 +2519,12 @@ 700396EB25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F67026A35D610082F6D6 /* URLCache.swift in Sources */, 70572672259033A700F0ADD5 /* ParseFileManager.swift in Sources */, + 709A148828396B1D00BF85E5 /* ParseField.swift in Sources */, 707A3C2125B14BD0000D215C /* ParseApple.swift in Sources */, 70F03A352780CA4D00E5AFB4 /* ParseGitHub.swift in Sources */, 703B095E26BF481F005A112F /* ParseFacebook+async.swift in Sources */, F97B462324D9C6F200F4A88B /* ParseKeyValueStore.swift in Sources */, + 709A147E283949D100BF85E5 /* ParseSchema.swift in Sources */, 703B090326BD9652005A112F /* ParseAnalytics+async.swift in Sources */, 703B094026BF47AC005A112F /* ParseApple+async.swift in Sources */, F97B45E724D9C6F200F4A88B /* Query.swift in Sources */, @@ -2796,6 +2820,7 @@ F97B465D24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A625A119430052CB31 /* Operations.swift in Sources */, 91BB8FD22690BA70005A6BA5 /* QueryObservable.swift in Sources */, + 709A148528395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769B26BD917500F86F71 /* Query+async.swift in Sources */, 703B095126BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3B25D998D90048EC1B /* ParseLDAP.swift in Sources */, @@ -2834,10 +2859,12 @@ 700396ED25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F67226A35D620082F6D6 /* URLCache.swift in Sources */, 70572674259033A700F0ADD5 /* ParseFileManager.swift in Sources */, + 709A148A28396B1D00BF85E5 /* ParseField.swift in Sources */, 707A3C2325B14BD0000D215C /* ParseApple.swift in Sources */, 70F03A372780CA4E00E5AFB4 /* ParseGitHub.swift in Sources */, 703B096026BF481F005A112F /* ParseFacebook+async.swift in Sources */, F97B462124D9C6F200F4A88B /* ParseStorage.swift in Sources */, + 709A1480283949D100BF85E5 /* ParseSchema.swift in Sources */, 703B090526BD9652005A112F /* ParseAnalytics+async.swift in Sources */, 703B094226BF47AC005A112F /* ParseApple+async.swift in Sources */, F97B466724D9C88600F4A88B /* SecureStorage.swift in Sources */, @@ -2936,6 +2963,7 @@ F97B465C24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A525A119430052CB31 /* Operations.swift in Sources */, 91BB8FD12690BA70005A6BA5 /* QueryObservable.swift in Sources */, + 709A148428395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769A26BD917500F86F71 /* Query+async.swift in Sources */, 703B095026BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3A25D998D90048EC1B /* ParseLDAP.swift in Sources */, @@ -2974,10 +3002,12 @@ 700396EC25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F67126A35D620082F6D6 /* URLCache.swift in Sources */, 70572673259033A700F0ADD5 /* ParseFileManager.swift in Sources */, + 709A148928396B1D00BF85E5 /* ParseField.swift in Sources */, 707A3C2225B14BD0000D215C /* ParseApple.swift in Sources */, 70F03A362780CA4D00E5AFB4 /* ParseGitHub.swift in Sources */, 703B095F26BF481F005A112F /* ParseFacebook+async.swift in Sources */, F97B462024D9C6F200F4A88B /* ParseStorage.swift in Sources */, + 709A147F283949D100BF85E5 /* ParseSchema.swift in Sources */, 703B090426BD9652005A112F /* ParseAnalytics+async.swift in Sources */, 703B094126BF47AC005A112F /* ParseApple+async.swift in Sources */, F97B466624D9C88600F4A88B /* SecureStorage.swift in Sources */, diff --git a/Sources/ParseSwift/API/API.swift b/Sources/ParseSwift/API/API.swift index 00023b4e1..477f1af35 100644 --- a/Sources/ParseSwift/API/API.swift +++ b/Sources/ParseSwift/API/API.swift @@ -44,6 +44,11 @@ public struct API { case aggregate(className: String) case config case health + case schemas + case schema(className: String) + case purge(className: String) + case triggers + case trigger(name: String, className: String) case any(String) var urlComponent: String { @@ -94,6 +99,16 @@ public struct API { return "/config" case .health: return "/health" + case .schemas: + return "/schemas" + case .schema(let className): + return "/sessions/\(className)" + case .purge(let className): + return "/purge/\(className)" + case .triggers: + return "/hooks/triggers" + case .trigger(let name, let className): + return "/hooks/triggers/\(className)/\(name)" case .any(let path): return path } diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index e46beb043..3ba412338 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -875,7 +875,7 @@ extension ParseUser { ) { var options = options options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { + do { try fetchCommand(include: includeKeys) .executeAsync(options: options, callbackQueue: callbackQueue) { result in diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift new file mode 100644 index 000000000..13abc579d --- /dev/null +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -0,0 +1,73 @@ +// +// ParseField.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +struct ParseField: Codable { + var type: ParseFieldType + var required: Bool + var defaultValue: AnyCodable? + var targetClass: String? + + init(type: ParseFieldType, + target: T? = nil, + options: ParseFieldOptions) where T: ParseObject, V: Codable { + self.type = type + self.targetClass = target?.className + self.required = options.required + self.defaultValue = AnyCodable(options.defaultValue) + } + + init(type: ParseFieldType, + target: Pointer? = nil, + options: ParseFieldOptions) where T: ParseObject, V: Codable { + self.type = type + self.targetClass = target?.className + self.required = options.required + self.defaultValue = AnyCodable(options.defaultValue) + } + + init(type: ParseFieldType, + target: T? = nil, + options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { + self.type = type + self.targetClass = target?.className + self.required = options.required + self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + } + + init(type: ParseFieldType, + target: Pointer? = nil, + options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { + self.type = type + self.targetClass = target?.className + self.required = options.required + self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + } +} + +public struct ParseFieldOptions: Codable { + var required: Bool = false + var defaultValue: V? +} + +public enum ParseFieldType: String, Codable { + case string = "String" + case number = "Number" + case boolean = "Boolean" + case date = "Date" + case file = "File" + case geoPoint = "GeoPoint" + case polygon = "Polygon" + case array = "Array" + case object = "Object" + case pointer = "Pointer" + case relation = "Relation" + case bytes = "Bytes" + case acl = "ACL" +} diff --git a/Sources/ParseSwift/Types/ParseSchema+async.swift b/Sources/ParseSwift/Types/ParseSchema+async.swift new file mode 100644 index 000000000..ce9008600 --- /dev/null +++ b/Sources/ParseSwift/Types/ParseSchema+async.swift @@ -0,0 +1,30 @@ +// +// ParseSchema+async.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +#if compiler(>=5.5.2) && canImport(_Concurrency) +import Foundation + +public extension ParseSchema { + /** + Fetches the `ParseSchema` *aynchronously* with the current data from the server and sets an error if one occurs. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - important: If an object fetched has the same objectId as current, it will automatically update the current. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func fetch(options: API.Options = []) async throws -> Self { + try await withCheckedThrowingContinuation { continuation in + self.fetch(options: options, + completion: continuation.resume) + } + } +} + +#endif diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift new file mode 100644 index 000000000..ca1af3815 --- /dev/null +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -0,0 +1,361 @@ +// +// ParseSchema.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +/** + `ParseSchema` is a local representation of a session. + This protocol conforms to `ParseObject` and retains the + same functionality. + */ +public struct ParseSchema: ParseType, Decodable { + /* associatedtype SchemaObject: ParseObject + + /// The session token for this session. + var className: String { get set } + /// The session token for this session. + var fields: [String: ParseField]? { get set } + /// The session token for this session. + var indexes: [String: [String: Int] ]? { get set } + /// The session token for this session. + var classLevelPermissions: [String: Codable]? { get set } + + init(className: String) */ + var className: String + /// The session token for this session. + internal var fields: [String: ParseField]? + /// The session token for this session. + internal var indexes: [String: [String: Int]]? + /// The session token for this session. + // internal var classLevelPermissions: [String: Codable]? +} + +// MARK: Default Implementations +public extension ParseSchema { + static var className: String { + T.className + } + + init() { + self.init(className: T.className) + } + + func addField(_ name: String, + type: ParseFieldType, + options: ParseFieldOptions) -> Self { + var mutableSchema = self + /* switch type { + case .string: + <#code#> + case .number: + <#code#> + case .boolean: + <#code#> + case .date: + <#code#> + case .file: + <#code#> + case .geoPoint: + <#code#> + case .polygon: + <#code#> + case .array: + <#code#> + case .object: + <#code#> + case .pointer: + <#code#> + case .relation: + <#code#> + case .bytes: + <#code#> + case .acl: + <#code#> + } */ + mutableSchema.fields + + return mutableSchema + } + + func addPointer(_ name: String, + target: T, + options: ParseFieldOptions) -> Self where T: ParseObject { + let field = ParseField(type: .pointer, target: target, options: options) + var mutableSchema = self + mutableSchema.fields[name] = field + + return mutableSchema + } +} + +// MARK: Convenience +extension ParseSchema { + static var endpoint: API.Endpoint { + .schema(className: className) + } + + static var endpointPurge: API.Endpoint { + .purge(className: className) + } + + var endpoint: API.Endpoint { + .schema(className: className) + } + + var endpointPurge: API.Endpoint { + .purge(className: className) + } +} + +// MARK: Fetchable +extension ParseSchema { + + /** + Fetches the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func fetch(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try fetchCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + func fetchCommand() throws -> API.Command { + + return API.Command(method: .GET, + path: endpoint) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } +} + +// MARK: Savable +extension ParseSchema { + + /** + Creates the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func create(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try createCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + /** + Updates the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func update(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try updateCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + func createCommand() throws -> API.Command { + + return API.Command(method: .POST, + path: endpoint, + body: self) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } + + func updateCommand() throws -> API.Command { + + API.Command(method: .PUT, + path: endpoint, + body: self) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } +} + +// MARK: Deletable +extension ParseSchema { + + /** + Deletes all objects in the `ParseSchema` *asynchronously* and executes the given callback block. + + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func purge( + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try deleteCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) + } + } + } + } catch let error as ParseError { + callbackQueue.async { + completion(.failure(error)) + } + } catch { + callbackQueue.async { + completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + } + } + } + + /** + Deletes the `ParseSchema` *asynchronously* and executes the given callback block. + + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` + currently contains objects, run `purge()` first. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func delete( + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try deleteCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) + } + } + } + } catch let error as ParseError { + callbackQueue.async { + completion(.failure(error)) + } + } catch { + callbackQueue.async { + completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + } + } + } + + func purgeCommand() throws -> API.Command { + + API.Command(method: .DELETE, + path: endpointPurge) { (data) -> NoBody in + let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) + if let error = error { + throw error + } else { + return NoBody() + } + } + } + + func deleteCommand() throws -> API.Command { + + API.Command(method: .DELETE, + path: endpoint, + body: self) { (data) -> NoBody in + let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) + if let error = error { + throw error + } else { + return NoBody() + } + } + } +} From fa3c29b83b945232f2f8dfe6eab8d7b1f4134524 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 21 May 2022 18:04:44 -0400 Subject: [PATCH 02/55] more updates --- .../3rd Party/ParseApple/ParseApple.swift | 2 +- .../ParseFacebook/ParseFacebook.swift | 2 +- .../3rd Party/ParseGithub/ParseGitHub.swift | 2 +- .../3rd Party/ParseGoogle/ParseGoogle.swift | 2 +- .../3rd Party/ParseLDAP/ParseLDAP.swift | 2 +- .../ParseLinkedIn/ParseLinkedIn.swift | 2 +- .../3rd Party/ParseTwitter/ParseTwitter.swift | 2 +- .../Protocols/ParseAuthentication.swift | 4 +- Sources/ParseSwift/Coding/ParseEncoder.swift | 2 +- Sources/ParseSwift/Objects/ParseObject.swift | 4 +- Sources/ParseSwift/Parse.swift | 12 +- Sources/ParseSwift/Types/ParseACL.swift | 32 ++--- .../ParseSwift/Types/ParseConfig+async.swift | 2 +- Sources/ParseSwift/Types/ParseConfig.swift | 2 +- Sources/ParseSwift/Types/ParseField.swift | 50 ++++++-- Sources/ParseSwift/Types/ParseSchema.swift | 113 ++++++++++-------- Sources/ParseSwift/Types/Pointer.swift | 6 +- .../ParseSwift/Types/QueryConstraint.swift | 6 +- Tests/ParseSwiftTests/ParseObjectTests.swift | 4 +- 19 files changed, 143 insertions(+), 108 deletions(-) diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift index 5f8109262..3e960a8cb 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift @@ -39,7 +39,7 @@ public struct ParseApple: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.token.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift index b0fab5c18..6a7e720f8 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift @@ -53,7 +53,7 @@ public struct ParseFacebook: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil else { return false diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift index 74c10b526..659f3f416 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift @@ -38,7 +38,7 @@ public struct ParseGitHub: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.accessToken.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift index 0268e6086..272331b04 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift @@ -43,7 +43,7 @@ public struct ParseGoogle: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil else { return false diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift index 0d029d058..fbdf633cf 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift @@ -33,7 +33,7 @@ public struct ParseLDAP: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.password.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift index 19db7b906..fba77c3ea 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift @@ -41,7 +41,7 @@ public struct ParseLinkedIn: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.accessToken.rawValue] != nil, diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift index ca6e9271a..bc68a3344 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift @@ -54,7 +54,7 @@ public struct ParseTwitter: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, `false` otherwise. + /// - returns: `true` if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.consumerKey.rawValue] != nil, diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift index bb898e62c..ee07bec16 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift @@ -59,7 +59,7 @@ public protocol ParseAuthentication: Codable { Whether the `ParseUser` is logged in with the respective authentication type. - parameter user: The `ParseUser` to check authentication type. The user must be logged in on this device. - returns: `true` if the `ParseUser` is logged in via the repective - authentication type. `false` if the user is not. + authentication type. **false** if the user is not. */ func isLinked(with user: AuthenticatedUser) -> Bool @@ -307,7 +307,7 @@ public extension ParseUser { Whether the `ParseUser` is logged in with the respective authentication string type. - parameter type: The authentication type to check. The user must be logged in on this device. - returns: `true` if the `ParseUser` is logged in via the repective - authentication type. `false` if the user is not. + authentication type. **false** if the user is not. */ func isLinked(with type: String) -> Bool { guard let authData = self.authData?[type] else { diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift index c42fcb7ce..05710f6bd 100644 --- a/Sources/ParseSwift/Coding/ParseEncoder.swift +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -233,7 +233,7 @@ internal class _ParseEncoder: JSONEncoder, Encoder { /// Returns whether a new element can be encoded at this coding path. /// - /// `true` if an element has not yet been encoded at this coding path; `false` otherwise. + /// `true` if an element has not yet been encoded at this coding path; **false** otherwise. var canEncodeNewValue: Bool { // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index e9b511bca..5942ab009 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -85,7 +85,7 @@ public protocol ParseObject: Objectable, Determines if a `KeyPath` of the current `ParseObject` should be restored by comparing it to another `ParseObject`. - parameter original: The original `ParseObject`. - - returns: Returns a `true` if the keyPath should be restored or `false` otherwise. + - returns: Returns a `true` if the keyPath should be restored or **false** otherwise. */ func shouldRestoreKey(_ key: KeyPath, original: Self) -> Bool where W: Equatable @@ -169,7 +169,7 @@ public extension ParseObject { /** Determines if two objects have the same objectId. - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: T) -> Bool { return other.className == className && other.objectId == objectId && objectId != nil diff --git a/Sources/ParseSwift/Parse.swift b/Sources/ParseSwift/Parse.swift index 2381cf360..dcacd3242 100644 --- a/Sources/ParseSwift/Parse.swift +++ b/Sources/ParseSwift/Parse.swift @@ -53,11 +53,11 @@ public struct ParseConfiguration { /// If your app previously used the iOS Objective-C SDK, setting this value /// to `true` will attempt to migrate relevant data stored in the Keychain to - /// ParseSwift. Defaults to `false`. + /// ParseSwift. Defaults to **false**. public internal(set) var isMigratingFromObjcSDK: Bool = false /// Deletes the Parse Keychain when the app is running for the first time. - /// Defaults to `false`. + /// Defaults to **false**. public internal(set) var isDeletingKeychainIfNeeded: Bool = false /// Maximum number of times to try to connect to Parse Server. @@ -91,9 +91,9 @@ public struct ParseConfiguration { - parameter cacheMemoryCapacity: The memory capacity of the cache, in bytes. Defaults to 512KB. - parameter cacheDiskCapacity: The disk capacity of the cache, in bytes. Defaults to 10MB. - parameter migratingFromObjcSDK: If your app previously used the iOS Objective-C SDK, setting this value - to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to `false`. + to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. - parameter deletingKeychainIfNeeded: Deletes the Parse Keychain when the app is running for the first time. - Defaults to `false`. + Defaults to **false**. - parameter httpAdditionalHeaders: A dictionary of additional headers to send with requests. See Apple's [documentation](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411532-httpadditionalheaders) for more info. @@ -256,9 +256,9 @@ public struct ParseSwift { - parameter cacheMemoryCapacity: The memory capacity of the cache, in bytes. Defaults to 512KB. - parameter cacheDiskCapacity: The disk capacity of the cache, in bytes. Defaults to 10MB. - parameter migratingFromObjcSDK: If your app previously used the iOS Objective-C SDK, setting this value - to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to `false`. + to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. - parameter deletingKeychainIfNeeded: Deletes the Parse Keychain when the app is running for the first time. - Defaults to `false`. + Defaults to **false**. - parameter httpAdditionalHeaders: A dictionary of additional headers to send with requests. See Apple's [documentation](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411532-httpadditionalheaders) for more info. diff --git a/Sources/ParseSwift/Types/ParseACL.swift b/Sources/ParseSwift/Types/ParseACL.swift index b58fc3cbb..61ece4801 100644 --- a/Sources/ParseSwift/Types/ParseACL.swift +++ b/Sources/ParseSwift/Types/ParseACL.swift @@ -75,7 +75,7 @@ public struct ParseACL: ParseType, Returns true if a particular key has a specific access level. - parameter key: The key of the `ParseUser` or `ParseRole` for which to retrieve access. - parameter access: The type of access. - - returns: `true` if the `key` has *explicit* access, otherwise `false`. + - returns: `true` if the `key` has *explicit* access, otherwise **false**. */ func get(_ key: String, access: Access) -> Bool { guard let acl = acl else { // no acl, all open! @@ -87,11 +87,11 @@ public struct ParseACL: ParseType, // MARK: ParseUser /** Gets whether the given `objectId` is *explicitly* allowed to read this object. - Even if this returns `false`, the user may still be able to access it if `publicReadAccess` returns `true` + Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns `true` or if the user belongs to a role that has access. - parameter objectId: The `ParseUser.objectId` of the user for which to retrieve access. - - returns: `true` if the user with this `objectId` has *explicit* read access, otherwise `false`. + - returns: `true` if the user with this `objectId` has *explicit* read access, otherwise **false**. */ public func getReadAccess(objectId: String) -> Bool { get(objectId, access: .read) @@ -99,11 +99,11 @@ public struct ParseACL: ParseType, /** Gets whether the given `ParseUser` is *explicitly* allowed to read this object. - Even if this returns `false`, the user may still be able to access it if `publicReadAccess` returns `true` + Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns `true` or if the user belongs to a role that has access. - parameter user: The `ParseUser` for which to retrieve access. - - returns: `true` if the user with this `ParseUser` has *explicit* read access, otherwise `false`. + - returns: `true` if the user with this `ParseUser` has *explicit* read access, otherwise **false**. */ public func getReadAccess(user: T) -> Bool where T: ParseUser { if let objectId = user.objectId { @@ -119,7 +119,7 @@ public struct ParseACL: ParseType, or if the user belongs to a role that has access. - parameter objectId: The `ParseUser.objectId` of the user for which to retrieve access. - - returns: `true` if the user with this `ParseUser.objectId` has *explicit* write access, otherwise `false`. + - returns: `true` if the user with this `ParseUser.objectId` has *explicit* write access, otherwise **false**. */ public func getWriteAccess(objectId: String) -> Bool { return get(objectId, access: .write) @@ -131,7 +131,7 @@ public struct ParseACL: ParseType, or if the user belongs to a role that has access. - parameter user: The `ParseUser` of the user for which to retrieve access. - - returns: `true` if the `ParseUser` has *explicit* write access, otherwise `false`. + - returns: `true` if the `ParseUser` has *explicit* write access, otherwise **false**. */ public func getWriteAccess(user: T) -> Bool where T: ParseUser { if let objectId = user.objectId { @@ -189,10 +189,10 @@ public struct ParseACL: ParseType, /** Get whether users belonging to the role with the given name are allowed to read this object. - Even if this returns `false`, the role may still be able to read it if a parent role has read access. + Even if this returns **false**, the role may still be able to read it if a parent role has read access. - parameter roleName: The name of the role. - - returns: `true` if the role has read access, otherwise `false`. + - returns: `true` if the role has read access, otherwise **false**. */ public func getReadAccess(roleName: String) -> Bool { get(toRole(roleName: roleName), access: .read) @@ -200,10 +200,10 @@ public struct ParseACL: ParseType, /** Get whether users belonging to the role are allowed to read this object. - Even if this returns `false`, the role may still be able to read it if a parent role has read access. + Even if this returns **false**, the role may still be able to read it if a parent role has read access. - parameter role: The `ParseRole` to get access for. - - returns: `true` if the `ParseRole` has read access, otherwise `false`. + - returns: `true` if the `ParseRole` has read access, otherwise **false**. */ public func getReadAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } @@ -212,10 +212,10 @@ public struct ParseACL: ParseType, /** Get whether users belonging to the role with the given name are allowed to write this object. - Even if this returns `false`, the role may still be able to write it if a parent role has write access. + Even if this returns **false**, the role may still be able to write it if a parent role has write access. - parameter roleName: The name of the role. - - returns: `true` if the role has read access, otherwise `false`. + - returns: `true` if the role has read access, otherwise **false**. */ public func getWriteAccess(roleName: String) -> Bool { get(toRole(roleName: roleName), access: .write) @@ -223,10 +223,10 @@ public struct ParseACL: ParseType, /** Get whether users belonging to the role are allowed to write this object. - Even if this returns `false`, the role may still be able to write it if a parent role has write access. + Even if this returns **false**, the role may still be able to write it if a parent role has write access. - parameter role: The `ParseRole` to get access for. - - returns: `true` if the role has read access, otherwise `false`. + - returns: `true` if the role has read access, otherwise **false**. */ public func getWriteAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } @@ -368,7 +368,7 @@ extension ParseACL { - parameter withAccessForCurrentUser: If `true`, the `ACL` that is applied to newly-created instance of `ParseObject` will provide read and write access to the `ParseUser.+currentUser` at the time of creation. - - If `false`, the provided `acl` will be used without modification. + - If **false**, the provided `acl` will be used without modification. - If `acl` is `nil`, this value is ignored. - returns: Updated defaultACL diff --git a/Sources/ParseSwift/Types/ParseConfig+async.swift b/Sources/ParseSwift/Types/ParseConfig+async.swift index 4b35fbae2..e04de6182 100644 --- a/Sources/ParseSwift/Types/ParseConfig+async.swift +++ b/Sources/ParseSwift/Types/ParseConfig+async.swift @@ -33,7 +33,7 @@ public extension ParseConfig { /** Update the Config *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: `true` if saved, `false` if save is unsuccessful. + - returns: `true` if saved, **false** if save is unsuccessful. - throws: An error of type `ParseError`. */ func save(options: API.Options = []) async throws -> Bool { diff --git a/Sources/ParseSwift/Types/ParseConfig.swift b/Sources/ParseSwift/Types/ParseConfig.swift index 355b95ec8..835d718b3 100644 --- a/Sources/ParseSwift/Types/ParseConfig.swift +++ b/Sources/ParseSwift/Types/ParseConfig.swift @@ -72,7 +72,7 @@ extension ParseConfig { /** Update the Config *synchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns `true` if updated, `false` otherwise. + - returns: Returns `true` if updated, **false** otherwise. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index 13abc579d..3ca7e1028 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -10,40 +10,64 @@ import Foundation struct ParseField: Codable { var type: ParseFieldType - var required: Bool + var required: Bool? var defaultValue: AnyCodable? var targetClass: String? - init(type: ParseFieldType, - target: T? = nil, - options: ParseFieldOptions) where T: ParseObject, V: Codable { + init(type: ParseFieldType, options: ParseFieldOptions) where V: Codable { + self.type = type + self.required = options.required + self.defaultValue = AnyCodable(options.defaultValue) + } + + init(type: ParseFieldType, options: ParseFieldOptions) throws where V: ParseObject { + self.type = type + self.required = options.required + self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + } + + init(type: ParseFieldType, + target: T? = nil) where T: ParseObject { + self.type = type + self.targetClass = target?.className + } + + init(type: ParseFieldType, + target: Pointer? = nil) where T: ParseObject { + self.type = type + self.targetClass = target?.className + } + + init(type: ParseFieldType, + target: T? = nil, + options: ParseFieldOptions) where T: ParseObject, V: Codable { self.type = type self.targetClass = target?.className self.required = options.required self.defaultValue = AnyCodable(options.defaultValue) } - init(type: ParseFieldType, - target: Pointer? = nil, - options: ParseFieldOptions) where T: ParseObject, V: Codable { + init(type: ParseFieldType, + target: Pointer? = nil, + options: ParseFieldOptions) where T: ParseObject, V: Codable { self.type = type self.targetClass = target?.className self.required = options.required self.defaultValue = AnyCodable(options.defaultValue) } - init(type: ParseFieldType, - target: T? = nil, - options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { + init(type: ParseFieldType, + target: T? = nil, + options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { self.type = type self.targetClass = target?.className self.required = options.required self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) } - init(type: ParseFieldType, - target: Pointer? = nil, - options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { + init(type: ParseFieldType, + target: Pointer? = nil, + options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { self.type = type self.targetClass = target?.className self.required = options.required diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index ca1af3815..2bfe8206d 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -9,28 +9,20 @@ import Foundation /** - `ParseSchema` is a local representation of a session. - This protocol conforms to `ParseObject` and retains the - same functionality. + `ParseSchema` is used for handeling your schemas. + - requires: `.useMasterKey` has to be available. */ public struct ParseSchema: ParseType, Decodable { - /* associatedtype SchemaObject: ParseObject - /// The session token for this session. - var className: String { get set } - /// The session token for this session. - var fields: [String: ParseField]? { get set } - /// The session token for this session. - var indexes: [String: [String: Int] ]? { get set } - /// The session token for this session. - var classLevelPermissions: [String: Codable]? { get set } - - init(className: String) */ + /// The class name of the Schema. var className: String + /// The session token for this session. internal var fields: [String: ParseField]? + /// The session token for this session. internal var indexes: [String: [String: Int]]? + /// The session token for this session. // internal var classLevelPermissions: [String: Codable]? } @@ -45,50 +37,69 @@ public extension ParseSchema { self.init(className: T.className) } - func addField(_ name: String, - type: ParseFieldType, - options: ParseFieldOptions) -> Self { - var mutableSchema = self - /* switch type { - case .string: - <#code#> - case .number: - <#code#> - case .boolean: - <#code#> - case .date: - <#code#> - case .file: - <#code#> - case .geoPoint: - <#code#> - case .polygon: - <#code#> - case .array: - <#code#> - case .object: - <#code#> + /** + Add a Field to Create/Update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. Defaults to **nil** + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining.. + */ + func addField(_ name: String, + type: ParseFieldType, + target: T? = nil, + options: ParseFieldOptions) throws -> Self where T: ParseObject { + switch type { case .pointer: - <#code#> + return try addPointer(name, target: target, options: options) case .relation: - <#code#> - case .bytes: - <#code#> - case .acl: - <#code#> - } */ - mutableSchema.fields - - return mutableSchema + return try addRelation(name, target: target) + default: + var mutableSchema = self + let field = ParseField(type: type, options: options) + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } } func addPointer(_ name: String, - target: T, - options: ParseFieldOptions) -> Self where T: ParseObject { + target: T?, + options: ParseFieldOptions) throws -> Self where T: ParseObject { + guard let target = target else { + throw ParseError(code: .unknownError, message: "Target must not be nil") + } + let field = ParseField(type: .pointer, target: target, options: options) var mutableSchema = self - mutableSchema.fields[name] = field - + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } + + func addRelation(_ name: String, + target: T?) throws -> Self where T: ParseObject { + guard let target = target else { + throw ParseError(code: .unknownError, message: "Target must not be nil") + } + + let field = ParseField(type: .relation, target: target) + var mutableSchema = self + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + return mutableSchema } } diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index 62e8760b0..1edcf3dfa 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -13,7 +13,7 @@ extension ParsePointer { /** Determines if two objects have the same objectId. - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: ParsePointer) -> Bool { return other.className == className && other.objectId == objectId @@ -81,7 +81,7 @@ public extension Pointer { /** Determines if a `ParseObject` and `Pointer`have the same `objectId`. - parameter as: `ParseObject` to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: T) -> Bool { return other.className == className && other.objectId == objectId @@ -90,7 +90,7 @@ public extension Pointer { /** Determines if two `Pointer`'s have the same `objectId`. - parameter as: `Pointer` to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: Self) -> Bool { return other.className == className && other.objectId == objectId diff --git a/Sources/ParseSwift/Types/QueryConstraint.swift b/Sources/ParseSwift/Types/QueryConstraint.swift index 75195718f..6edfb7066 100644 --- a/Sources/ParseSwift/Types/QueryConstraint.swift +++ b/Sources/ParseSwift/Types/QueryConstraint.swift @@ -511,7 +511,7 @@ public func near(key: String, geoPoint: ParseGeoPoint) -> QueryConstraint { - parameter key: The key to be constrained. - parameter geoPoint: The reference point as a `ParseGeoPoint`. - parameter distance: Maximum distance in radians. - - parameter sorted: `true` if results should be sorted by distance ascending, `false` is no sorting is required. + - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ @@ -537,7 +537,7 @@ public func withinRadians(key: String, - parameter key: The key to be constrained. - parameter geoPoint: The reference point represented as a `ParseGeoPoint`. - parameter distance: Maximum distance in miles. - - parameter sorted: `true` if results should be sorted by distance ascending, `false` is no sorting is required. + - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ @@ -558,7 +558,7 @@ public func withinMiles(key: String, - parameter key: The key to be constrained. - parameter geoPoint: The reference point represented as a `ParseGeoPoint`. - parameter distance: Maximum distance in kilometers. - - parameter sorted: `true` if results should be sorted by distance ascending, `false` is no sorting is required. + - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index f36d14d56..f02a1414e 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -156,7 +156,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length - parameter lhs: first object to compare - parameter rhs: second object to compare - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ public static func == (lhs: ParseObjectTests.GameScoreClass, rhs: ParseObjectTests.GameScoreClass) -> Bool { @@ -213,7 +213,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length - parameter lhs: first object to compare - parameter rhs: second object to compare - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. */ public static func == (lhs: ParseObjectTests.GameClass, rhs: ParseObjectTests.GameClass) -> Bool { lhs.hasSameObjectId(as: rhs) From 5ae88c333c1efed1474602de99ac43224c4a2160 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 21 May 2022 18:54:31 -0400 Subject: [PATCH 03/55] more updates --- ParseSwift.xcodeproj/project.pbxproj | 10 + Sources/ParseSwift/Operations/Add.swift | 2 +- .../ParseSwift/Operations/AddRelation.swift | 2 +- Sources/ParseSwift/Operations/AddUnique.swift | 2 +- Sources/ParseSwift/Operations/Delete.swift | 2 +- Sources/ParseSwift/Operations/Increment.swift | 2 +- Sources/ParseSwift/Operations/Operation.swift | 19 ++ Sources/ParseSwift/Operations/Remove.swift | 2 +- .../Operations/RemoveRelation.swift | 2 +- Sources/ParseSwift/Types/ParseField.swift | 7 +- Sources/ParseSwift/Types/ParseSchema.swift | 209 +++++++++++++++++- 11 files changed, 239 insertions(+), 20 deletions(-) create mode 100644 Sources/ParseSwift/Operations/Operation.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 5c56024c2..8c9fc79c8 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -354,6 +354,10 @@ 709A148828396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; 709A148928396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; 709A148A28396B1D00BF85E5 /* ParseField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148628396B1C00BF85E5 /* ParseField.swift */; }; + 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; + 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; + 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; + 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -983,6 +987,7 @@ 709A147C283949D100BF85E5 /* ParseSchema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchema.swift; sourceTree = ""; }; 709A148128395ED100BF85E5 /* ParseSchema+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+async.swift"; sourceTree = ""; }; 709A148628396B1C00BF85E5 /* ParseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseField.swift; sourceTree = ""; }; + 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1773,6 +1778,7 @@ F97B464524D9C78B00F4A88B /* Increment.swift */, F97B464424D9C78B00F4A88B /* Remove.swift */, 70C5509F25B4A9F600B5DBC2 /* RemoveRelation.swift */, + 709A148B2839A1DB00BF85E5 /* Operation.swift */, ); path = Operations; sourceTree = ""; @@ -2216,6 +2222,7 @@ F97B463724D9C74400F4A88B /* Responses.swift in Sources */, 916786E2259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346B9269B766C005727B6 /* CloudViewModel.swift in Sources */, + 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */, 7028373426BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503825B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2453,6 +2460,7 @@ F97B463824D9C74400F4A88B /* Responses.swift in Sources */, 916786E3259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BA269B766D005727B6 /* CloudViewModel.swift in Sources */, + 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */, 7028373526BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503925B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2793,6 +2801,7 @@ F97B45D524D9C6F200F4A88B /* AnyDecodable.swift in Sources */, 916786E5259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BC269B766D005727B6 /* CloudViewModel.swift in Sources */, + 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E924D9C6F200F4A88B /* Query.swift in Sources */, F97B463624D9C74400F4A88B /* URLSession.swift in Sources */, 7028373726BD8883007688C9 /* ParseObject+async.swift in Sources */, @@ -2936,6 +2945,7 @@ F97B45D424D9C6F200F4A88B /* AnyDecodable.swift in Sources */, 916786E4259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BB269B766D005727B6 /* CloudViewModel.swift in Sources */, + 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E824D9C6F200F4A88B /* Query.swift in Sources */, F97B463524D9C74400F4A88B /* URLSession.swift in Sources */, 7028373626BD8883007688C9 /* ParseObject+async.swift in Sources */, diff --git a/Sources/ParseSwift/Operations/Add.swift b/Sources/ParseSwift/Operations/Add.swift index 474368925..ebd08c536 100644 --- a/Sources/ParseSwift/Operations/Add.swift +++ b/Sources/ParseSwift/Operations/Add.swift @@ -9,6 +9,6 @@ import Foundation internal struct Add: Encodable where T: Encodable { - let __op: String = "Add" // swiftlint:disable:this identifier_name + let __op: Operation = .add // swiftlint:disable:this identifier_name let objects: [T] } diff --git a/Sources/ParseSwift/Operations/AddRelation.swift b/Sources/ParseSwift/Operations/AddRelation.swift index 840298ef9..e0206a42e 100644 --- a/Sources/ParseSwift/Operations/AddRelation.swift +++ b/Sources/ParseSwift/Operations/AddRelation.swift @@ -9,7 +9,7 @@ import Foundation internal struct AddRelation: Encodable where T: ParseObject { - let __op: String = "AddRelation" // swiftlint:disable:this identifier_name + let __op: Operation = .addRelation // swiftlint:disable:this identifier_name let objects: [Pointer] init(objects: [T]) throws { diff --git a/Sources/ParseSwift/Operations/AddUnique.swift b/Sources/ParseSwift/Operations/AddUnique.swift index 77606c83e..1e2f40d71 100644 --- a/Sources/ParseSwift/Operations/AddUnique.swift +++ b/Sources/ParseSwift/Operations/AddUnique.swift @@ -9,6 +9,6 @@ import Foundation internal struct AddUnique: Encodable where T: Encodable { - let __op: String = "AddUnique" // swiftlint:disable:this identifier_name + let __op: Operation = .addUnique // swiftlint:disable:this identifier_name let objects: [T] } diff --git a/Sources/ParseSwift/Operations/Delete.swift b/Sources/ParseSwift/Operations/Delete.swift index 715a4c823..020f72afd 100644 --- a/Sources/ParseSwift/Operations/Delete.swift +++ b/Sources/ParseSwift/Operations/Delete.swift @@ -9,5 +9,5 @@ import Foundation internal struct Delete: Encodable { - let __op: String = "Delete" // swiftlint:disable:this identifier_name + let __op: Operation = .delete // swiftlint:disable:this identifier_name } diff --git a/Sources/ParseSwift/Operations/Increment.swift b/Sources/ParseSwift/Operations/Increment.swift index d904a6e9f..b52fc4dc6 100644 --- a/Sources/ParseSwift/Operations/Increment.swift +++ b/Sources/ParseSwift/Operations/Increment.swift @@ -9,6 +9,6 @@ import Foundation internal struct Increment: Encodable { - let __op: String = "Increment" // swiftlint:disable:this identifier_name + let __op: Operation = .increment // swiftlint:disable:this identifier_name let amount: Int } diff --git a/Sources/ParseSwift/Operations/Operation.swift b/Sources/ParseSwift/Operations/Operation.swift new file mode 100644 index 000000000..496019696 --- /dev/null +++ b/Sources/ParseSwift/Operations/Operation.swift @@ -0,0 +1,19 @@ +// +// Operation.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +enum Operation: String, Codable { + case add = "Add" + case addRelation = "AddRelation" + case addUnique = "AddUnique" + case delete = "Delete" + case increment = "Increment" + case remove = "Remove" + case removeRelation = "RemoveRelation" +} diff --git a/Sources/ParseSwift/Operations/Remove.swift b/Sources/ParseSwift/Operations/Remove.swift index 7022b7c24..3455d7a01 100644 --- a/Sources/ParseSwift/Operations/Remove.swift +++ b/Sources/ParseSwift/Operations/Remove.swift @@ -9,6 +9,6 @@ import Foundation internal struct Remove: Encodable where T: Encodable { - let __op: String = "Remove" // swiftlint:disable:this identifier_name + let __op: Operation = .remove // swiftlint:disable:this identifier_name let objects: [T] } diff --git a/Sources/ParseSwift/Operations/RemoveRelation.swift b/Sources/ParseSwift/Operations/RemoveRelation.swift index 2d9b8e142..c72b8e9e8 100644 --- a/Sources/ParseSwift/Operations/RemoveRelation.swift +++ b/Sources/ParseSwift/Operations/RemoveRelation.swift @@ -9,7 +9,7 @@ import Foundation internal struct RemoveRelation: Encodable where T: ParseObject { - let __op: String = "RemoveRelation" // swiftlint:disable:this identifier_name + let __op: Operation = .removeRelation // swiftlint:disable:this identifier_name let objects: [Pointer] init(objects: [T]) throws { diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index 3ca7e1028..c86f59ebe 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -9,11 +9,16 @@ import Foundation struct ParseField: Codable { - var type: ParseFieldType + var __op: Operation? // swiftlint:disable:this identifier_name + var type: ParseFieldType? var required: Bool? var defaultValue: AnyCodable? var targetClass: String? + init(operation: Operation) { + __op = operation + } + init(type: ParseFieldType, options: ParseFieldOptions) where V: Codable { self.type = type self.required = options.required diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 2bfe8206d..9e0c0f57c 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -38,17 +38,19 @@ public extension ParseSchema { } /** - Add a Field to Create/Update a `ParseSchema`. + Add a Field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated on Parse Server. - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. Defaults to **nil** + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining.. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + - warning: The use of `options` requires Parse Server 3.7.0+. */ func addField(_ name: String, type: ParseFieldType, - target: T? = nil, + target: T, options: ParseFieldOptions) throws -> Self where T: ParseObject { switch type { case .pointer: @@ -56,18 +58,174 @@ public extension ParseSchema { case .relation: return try addRelation(name, target: target) default: - var mutableSchema = self - let field = ParseField(type: type, options: options) - if mutableSchema.fields != nil { - mutableSchema.fields?[name] = field - } else { - mutableSchema.fields = [name: field] - } + return addField(name, type: type, options: options) + } + } - return mutableSchema + /** + Add a Field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addField(_ name: String, + type: ParseFieldType, + options: ParseFieldOptions) -> Self where T: ParseObject { + var mutableSchema = self + let field = ParseField(type: type, options: options) + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] } + + return mutableSchema + } + + /** + Add a String field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addString(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .string, options: options) } + /** + Add a Number field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addNumber(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .number, options: options) + } + + /** + Add a Boolean field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addBoolean(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .boolean, options: options) + } + + /** + Add a Date field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addDate(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .date, options: options) + } + + /** + Add a File field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addFile(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .file, options: options) + } + + /** + Add a GeoPoint field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addGeoPoint(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .geoPoint, options: options) + } + + /** + Add a Polygon field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addPolygon(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .polygon, options: options) + } + + /** + Add an Object field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addObject(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .object, options: options) + } + + /** + Add a Bytes field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addBytes(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .bytes, options: options) + } + + /** + Add an Array field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addArray(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { + addField(name, type: .array, options: options) + } + + /** + Add a Pointer field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + Defaults to **nil**. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ func addPointer(_ name: String, target: T?, options: ParseFieldOptions) throws -> Self where T: ParseObject { @@ -86,6 +244,15 @@ public extension ParseSchema { return mutableSchema } + /** + Add a Relation field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + Defaults to **nil**. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + */ func addRelation(_ name: String, target: T?) throws -> Self where T: ParseObject { guard let target = target else { @@ -102,6 +269,24 @@ public extension ParseSchema { return mutableSchema } + + /** + Delete a field in the `ParseSchema`. + + - parameter name: Name of the field that will be deleted on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func deleteField(_ name: String) -> Self { + let field = ParseField(operation: .delete) + var mutableSchema = self + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } } // MARK: Convenience From f81fc1696f56fcd9ccdff42ab9954817e54f9e93 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 21 May 2022 20:13:17 -0400 Subject: [PATCH 04/55] addIndex and deleteIndex --- ParseSwift.xcodeproj/project.pbxproj | 20 ++++++ Sources/ParseSwift/Protocols/ParseIndex.swift | 16 +++++ .../ParseSwift/Protocols/ParseSchemable.swift | 11 ++++ Sources/ParseSwift/Types/ParseField.swift | 2 +- Sources/ParseSwift/Types/ParseSchema.swift | 64 +++++++++++++++---- 5 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 Sources/ParseSwift/Protocols/ParseIndex.swift create mode 100644 Sources/ParseSwift/Protocols/ParseSchemable.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 8c9fc79c8..3d39eec5a 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -358,6 +358,14 @@ 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; + 709A14912839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; + 709A14922839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; + 709A14932839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; + 709A14942839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; + 709A14962839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; + 709A14972839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; + 709A14982839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; + 709A14992839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -988,6 +996,8 @@ 709A148128395ED100BF85E5 /* ParseSchema+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+async.swift"; sourceTree = ""; }; 709A148628396B1C00BF85E5 /* ParseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseField.swift; sourceTree = ""; }; 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; + 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; + 709A14952839B72300BF85E5 /* ParseSchemable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemable.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1431,8 +1441,10 @@ F97B45C524D9C6F200F4A88B /* Fetchable.swift */, 705A9A2E25991C1400B3547F /* Fileable.swift */, 70BC988F252A5B5C00FF3074 /* Objectable.swift */, + 709A14902839A60600BF85E5 /* ParseIndex.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, + 709A14952839B72300BF85E5 /* ParseSchemable.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, 91BB8FCE2690BA70005A6BA5 /* QueryObservable.swift */, F97B45C724D9C6F200F4A88B /* Savable.swift */, @@ -2249,6 +2261,7 @@ F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A325A119430052CB31 /* Operations.swift in Sources */, 91BB8FCF2690BA70005A6BA5 /* QueryObservable.swift in Sources */, + 709A14912839A60600BF85E5 /* ParseIndex.swift in Sources */, 70F03A232780BDE200E5AFB4 /* ParseGoogle.swift in Sources */, 709A148228395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769826BD917500F86F71 /* Query+async.swift in Sources */, @@ -2328,6 +2341,7 @@ 7045769D26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B463324D9C74400F4A88B /* URLSession.swift in Sources */, F97B464E24D9C78B00F4A88B /* Add.swift in Sources */, + 709A14962839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095326BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9890252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FE24D9C6F200F4A88B /* ParseFile.swift in Sources */, @@ -2487,6 +2501,7 @@ F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A425A119430052CB31 /* Operations.swift in Sources */, 91BB8FD02690BA70005A6BA5 /* QueryObservable.swift in Sources */, + 709A14922839A60600BF85E5 /* ParseIndex.swift in Sources */, 7045769926BD917500F86F71 /* Query+async.swift in Sources */, 709A148328395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 703B094F26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, @@ -2566,6 +2581,7 @@ 7045769E26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B463424D9C74400F4A88B /* URLSession.swift in Sources */, F97B464F24D9C78B00F4A88B /* Add.swift in Sources */, + 709A14972839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095426BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9891252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FF24D9C6F200F4A88B /* ParseFile.swift in Sources */, @@ -2828,6 +2844,7 @@ 7085DD9726CBF3A70033B977 /* Documentation.docc in Sources */, F97B465D24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A625A119430052CB31 /* Operations.swift in Sources */, + 709A14942839A60600BF85E5 /* ParseIndex.swift in Sources */, 91BB8FD22690BA70005A6BA5 /* QueryObservable.swift in Sources */, 709A148528395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769B26BD917500F86F71 /* Query+async.swift in Sources */, @@ -2907,6 +2924,7 @@ 704576A026BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B464924D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */, + 709A14992839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095626BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9893252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465524D9C78C00F4A88B /* AddUnique.swift in Sources */, @@ -2972,6 +2990,7 @@ 7085DD9626CBF3A70033B977 /* Documentation.docc in Sources */, F97B465C24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A525A119430052CB31 /* Operations.swift in Sources */, + 709A14932839A60600BF85E5 /* ParseIndex.swift in Sources */, 91BB8FD12690BA70005A6BA5 /* QueryObservable.swift in Sources */, 709A148428395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769A26BD917500F86F71 /* Query+async.swift in Sources */, @@ -3051,6 +3070,7 @@ 7045769F26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B464824D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */, + 709A14982839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095526BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9892252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465424D9C78C00F4A88B /* AddUnique.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseIndex.swift b/Sources/ParseSwift/Protocols/ParseIndex.swift new file mode 100644 index 000000000..36381980b --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParseIndex.swift @@ -0,0 +1,16 @@ +// +// ParseIndex.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +/** + `ParseIndex` is used to create/update an index on a field of `ParseSchema`. + The "property name" should match the "field name" the index should be added to. + The "property value" should be the "type" of index to add which is usually a **String** or **Int*. +*/ +public protocol ParseIndex: Codable { } diff --git a/Sources/ParseSwift/Protocols/ParseSchemable.swift b/Sources/ParseSwift/Protocols/ParseSchemable.swift new file mode 100644 index 000000000..4ed5fd370 --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParseSchemable.swift @@ -0,0 +1,11 @@ +// +// ParseSchemable.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +protocol ParseSchemable: ParseType, Decodable { } diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index c86f59ebe..5b1e02fa3 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -18,7 +18,7 @@ struct ParseField: Codable { init(operation: Operation) { __op = operation } - + init(type: ParseFieldType, options: ParseFieldOptions) where V: Codable { self.type = type self.required = options.required diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 9e0c0f57c..642f12b01 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -12,7 +12,7 @@ import Foundation `ParseSchema` is used for handeling your schemas. - requires: `.useMasterKey` has to be available. */ -public struct ParseSchema: ParseType, Decodable { +public struct ParseSchema: ParseSchemable { /// The class name of the Schema. var className: String @@ -21,7 +21,7 @@ public struct ParseSchema: ParseType, Decodable { internal var fields: [String: ParseField]? /// The session token for this session. - internal var indexes: [String: [String: Int]]? + internal var indexes: [String: AnyCodable]? /// The session token for this session. // internal var classLevelPermissions: [String: Codable]? @@ -37,6 +37,26 @@ public extension ParseSchema { self.init(className: T.className) } + /** + Add a Field to create/update a `ParseSchema`. + + - parameter name: Name of the index that will be created/updated on Parse Server. + - parameter index: The `ParseIndex` to add that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func addIndex(_ name: String, + index: ParseIndex) -> Self { + var mutableSchema = self + + if mutableSchema.indexes != nil { + mutableSchema.indexes?[name] = AnyCodable(index) + } else { + mutableSchema.indexes = [name: AnyCodable(index)] + } + + return mutableSchema + } + /** Add a Field to create/update a `ParseSchema`. @@ -73,7 +93,7 @@ public extension ParseSchema { */ func addField(_ name: String, type: ParseFieldType, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { var mutableSchema = self let field = ParseField(type: type, options: options) if mutableSchema.fields != nil { @@ -94,7 +114,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addString(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .string, options: options) } @@ -107,7 +127,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addNumber(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .number, options: options) } @@ -120,7 +140,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addBoolean(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .boolean, options: options) } @@ -133,7 +153,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addDate(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .date, options: options) } @@ -146,7 +166,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addFile(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .file, options: options) } @@ -159,7 +179,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addGeoPoint(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .geoPoint, options: options) } @@ -172,7 +192,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addPolygon(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .polygon, options: options) } @@ -185,7 +205,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addObject(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .object, options: options) } @@ -198,7 +218,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addBytes(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .bytes, options: options) } @@ -211,7 +231,7 @@ public extension ParseSchema { - warning: The use of `options` requires Parse Server 3.7.0+. */ func addArray(_ name: String, - options: ParseFieldOptions) -> Self where T: ParseObject { + options: ParseFieldOptions) -> Self { addField(name, type: .array, options: options) } @@ -287,6 +307,24 @@ public extension ParseSchema { return mutableSchema } + + /** + Delete an index in the `ParseSchema`. + + - parameter name: Name of the index that will be deleted on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func deleteIndex(_ name: String) -> Self { + let index = AnyCodable(Delete()) + var mutableSchema = self + if mutableSchema.indexes != nil { + mutableSchema.indexes?[name] = index + } else { + mutableSchema.indexes = [name: index] + } + + return mutableSchema + } } // MARK: Convenience From c621cfed1d751fe743baf08348224761d25c86ad Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 21 May 2022 21:15:50 -0400 Subject: [PATCH 05/55] read only fields and indexes --- .../Contents.swift | 48 ++ ParseSwift.playground/contents.xcplayground | 1 + ParseSwift.xcodeproj/project.pbxproj | 10 + .../Protocols/ParseSchemable+async.swift | 30 + .../ParseSwift/Protocols/ParseSchemable.swift | 565 +++++++++++++++++- Sources/ParseSwift/Types/ParseField.swift | 37 ++ Sources/ParseSwift/Types/ParseSchema.swift | 32 +- 7 files changed, 718 insertions(+), 5 deletions(-) create mode 100644 ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift create mode 100644 Sources/ParseSwift/Protocols/ParseSchemable+async.swift diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift new file mode 100644 index 000000000..36a3d691a --- /dev/null +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -0,0 +1,48 @@ +//: [Previous](@previous) + +import PlaygroundSupport +import Foundation +import ParseSwift + +PlaygroundPage.current.needsIndefiniteExecution = true +initializeParse() + +//: Create your own value typed `ParseObject`. +struct GameScore: ParseObject { + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + //: Your own properties. + var points: Int? + + //: Implement your own version of merge + func merge(with object: Self) throws -> Self { + var updated = try mergeParse(with: object) + if updated.shouldRestoreKey(\.points, + original: object) { + updated.points = object.points + } + return updated + } +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the memberwise initializer. +extension GameScore { + + init(points: Int) { + self.points = points + } + + init(objectId: String?) { + self.objectId = objectId + } +} + +let schema = ParseSchema() +schema. +//: [Next](@next) diff --git a/ParseSwift.playground/contents.xcplayground b/ParseSwift.playground/contents.xcplayground index 774a9ea88..b37eba70a 100644 --- a/ParseSwift.playground/contents.xcplayground +++ b/ParseSwift.playground/contents.xcplayground @@ -20,5 +20,6 @@ + \ No newline at end of file diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 3d39eec5a..5ba2258a7 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -366,6 +366,10 @@ 709A14972839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; 709A14982839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; 709A14992839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; + 709A149B2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; + 709A149C2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; + 709A149D2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; + 709A149E2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -998,6 +1002,7 @@ 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A14952839B72300BF85E5 /* ParseSchemable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemable.swift; sourceTree = ""; }; + 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchemable+async.swift"; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1445,6 +1450,7 @@ 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, 709A14952839B72300BF85E5 /* ParseSchemable.swift */, + 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, 91BB8FCE2690BA70005A6BA5 /* QueryObservable.swift */, F97B45C724D9C6F200F4A88B /* Savable.swift */, @@ -2236,6 +2242,7 @@ 91F346B9269B766C005727B6 /* CloudViewModel.swift in Sources */, 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */, + 709A149B2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, 7028373426BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503825B406B800B5DBC2 /* ParseSession.swift in Sources */, 7044C1C825C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, @@ -2476,6 +2483,7 @@ 91F346BA269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */, + 709A149C2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, 7028373526BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503925B406B800B5DBC2 /* ParseSession.swift in Sources */, 7044C1C925C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, @@ -2819,6 +2827,7 @@ 91F346BC269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E924D9C6F200F4A88B /* Query.swift in Sources */, + 709A149E2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, F97B463624D9C74400F4A88B /* URLSession.swift in Sources */, 7028373726BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503B25B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2965,6 +2974,7 @@ 91F346BB269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E824D9C6F200F4A88B /* Query.swift in Sources */, + 709A149D2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, F97B463524D9C74400F4A88B /* URLSession.swift in Sources */, 7028373626BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503A25B406B800B5DBC2 /* ParseSession.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseSchemable+async.swift b/Sources/ParseSwift/Protocols/ParseSchemable+async.swift new file mode 100644 index 000000000..85526a998 --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParseSchemable+async.swift @@ -0,0 +1,30 @@ +// +// ParseSchemable+async.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +#if compiler(>=5.5.2) && canImport(_Concurrency) +import Foundation + +extension ParseSchemable { + /** + Fetches the `ParseSchema` *aynchronously* with the current data from the server and sets an error if one occurs. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - important: If an object fetched has the same objectId as current, it will automatically update the current. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func fetch(options: API.Options = []) async throws -> Self { + try await withCheckedThrowingContinuation { continuation in + self.fetch(options: options, + completion: continuation.resume) + } + } +} + +#endif diff --git a/Sources/ParseSwift/Protocols/ParseSchemable.swift b/Sources/ParseSwift/Protocols/ParseSchemable.swift index 4ed5fd370..b72ef55ee 100644 --- a/Sources/ParseSwift/Protocols/ParseSchemable.swift +++ b/Sources/ParseSwift/Protocols/ParseSchemable.swift @@ -8,4 +8,567 @@ import Foundation -protocol ParseSchemable: ParseType, Decodable { } +protocol ParseSchemable: ParseType, Decodable { + + /// The class name of the Schema. + var className: String { get set } + + /// The session token for this session. + var fields: [String: ParseField]? { get set } + + /// The session token for this session. + var indexes: [String: AnyCodable]? { get set } +} + +// MARK: Default Implementations +extension ParseSchemable { + + /** + Add a Field to create/update a `ParseSchema`. + + - parameter name: Name of the index that will be created/updated on Parse Server. + - parameter index: The `ParseIndex` to add that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + public func addIndex(_ name: String, + index: ParseIndex) -> Self { + var mutableSchema = self + + if mutableSchema.indexes != nil { + mutableSchema.indexes?[name] = AnyCodable(index) + } else { + mutableSchema.indexes = [name: AnyCodable(index)] + } + + return mutableSchema + } + + /** + Add a Field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addField(_ name: String, + type: ParseFieldType, + target: T, + options: ParseFieldOptions) throws -> Self where T: ParseObject { + switch type { + case .pointer: + return try addPointer(name, target: target, options: options) + case .relation: + return try addRelation(name, target: target) + default: + return addField(name, type: type, options: options) + } + } + + /** + Add a Field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addField(_ name: String, + type: ParseFieldType, + options: ParseFieldOptions) -> Self { + var mutableSchema = self + let field = ParseField(type: type, options: options) + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } + + /** + Add a String field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addString(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .string, options: options) + } + + /** + Add a Number field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addNumber(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .number, options: options) + } + + /** + Add a Boolean field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addBoolean(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .boolean, options: options) + } + + /** + Add a Date field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addDate(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .date, options: options) + } + + /** + Add a File field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addFile(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .file, options: options) + } + + /** + Add a GeoPoint field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addGeoPoint(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .geoPoint, options: options) + } + + /** + Add a Polygon field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addPolygon(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .polygon, options: options) + } + + /** + Add an Object field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addObject(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .object, options: options) + } + + /** + Add a Bytes field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addBytes(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .bytes, options: options) + } + + /** + Add an Array field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addArray(_ name: String, + options: ParseFieldOptions) -> Self { + addField(name, type: .array, options: options) + } + + /** + Add a Pointer field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + Defaults to **nil**. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + - warning: The use of `options` requires Parse Server 3.7.0+. + */ + func addPointer(_ name: String, + target: T?, + options: ParseFieldOptions) throws -> Self where T: ParseObject { + guard let target = target else { + throw ParseError(code: .unknownError, message: "Target must not be nil") + } + + let field = ParseField(type: .pointer, target: target, options: options) + var mutableSchema = self + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } + + /** + Add a Relation field to create/update a `ParseSchema`. + + - parameter name: Name of the field that will be created/updated on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + Defaults to **nil**. + - returns: A mutated instance of `ParseSchema` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addRelation(_ name: String, + target: T?) throws -> Self where T: ParseObject { + guard let target = target else { + throw ParseError(code: .unknownError, message: "Target must not be nil") + } + + let field = ParseField(type: .relation, target: target) + var mutableSchema = self + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } + + /** + Delete a field in the `ParseSchema`. + + - parameter name: Name of the field that will be deleted on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func deleteField(_ name: String) -> Self { + let field = ParseField(operation: .delete) + var mutableSchema = self + if mutableSchema.fields != nil { + mutableSchema.fields?[name] = field + } else { + mutableSchema.fields = [name: field] + } + + return mutableSchema + } + + /** + Delete an index in the `ParseSchema`. + + - parameter name: Name of the index that will be deleted on Parse Server. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func deleteIndex(_ name: String) -> Self { + let index = AnyCodable(Delete()) + var mutableSchema = self + if mutableSchema.indexes != nil { + mutableSchema.indexes?[name] = index + } else { + mutableSchema.indexes = [name: index] + } + + return mutableSchema + } +} + +// MARK: Convenience +extension ParseSchemable { + + var endpoint: API.Endpoint { + .schema(className: className) + } + + var endpointPurge: API.Endpoint { + .purge(className: className) + } +} + +// MARK: Fetchable +extension ParseSchemable { + + /** + Fetches the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func fetch(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try fetchCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + func fetchCommand() throws -> API.Command { + + return API.Command(method: .GET, + path: endpoint) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } +} + +// MARK: Savable +extension ParseSchemable { + + /** + Creates the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func create(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try createCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + /** + Updates the `ParseSchema` *asynchronously* and executes the given callback block. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func update(options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try updateCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) + } catch { + callbackQueue.async { + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } + } + } + } + + func createCommand() throws -> API.Command { + + return API.Command(method: .POST, + path: endpoint, + body: self) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } + + func updateCommand() throws -> API.Command { + + API.Command(method: .PUT, + path: endpoint, + body: self) { (data) -> Self in + try ParseCoding.jsonDecoder().decode(Self.self, from: data) + } + } +} + +// MARK: Deletable +extension ParseSchemable { + + /** + Deletes all objects in the `ParseSchema` *asynchronously* and executes the given callback block. + + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func purge( + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try deleteCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) + } + } + } + } catch let error as ParseError { + callbackQueue.async { + completion(.failure(error)) + } + } catch { + callbackQueue.async { + completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + } + } + } + + /** + Deletes the `ParseSchema` *asynchronously* and executes the given callback block. + + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` + currently contains objects, run `purge()` first. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + public func delete( + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + do { + try deleteCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) + } + } + } + } catch let error as ParseError { + callbackQueue.async { + completion(.failure(error)) + } + } catch { + callbackQueue.async { + completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + } + } + } + + func purgeCommand() throws -> API.Command { + + API.Command(method: .DELETE, + path: endpointPurge) { (data) -> NoBody in + let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) + if let error = error { + throw error + } else { + return NoBody() + } + } + } + + func deleteCommand() throws -> API.Command { + + API.Command(method: .DELETE, + path: endpoint, + body: self) { (data) -> NoBody in + let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) + if let error = error { + throw error + } else { + return NoBody() + } + } + } +} diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index 5b1e02fa3..19736ad10 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -80,23 +80,60 @@ struct ParseField: Codable { } } +// MARK: CustomDebugStringConvertible +extension ParseField { + public var debugDescription: String { + guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), + let descriptionString = String(data: descriptionData, encoding: .utf8) else { + return "()" + } + + return "(\(descriptionString))" + } +} + +// MARK: CustomStringConvertible +extension ParseField { + public var description: String { + debugDescription + } +} + +/// The options for a field in `ParseSchema` public struct ParseFieldOptions: Codable { + /// Specifies if a field is required. var required: Bool = false + + /// The default value for a field. var defaultValue: V? } +/// Field types available in `ParseSchema`. public enum ParseFieldType: String, Codable { + /// A string type. case string = "String" + /// A number type. case number = "Number" + /// A boolean type. case boolean = "Boolean" + /// A date type. case date = "Date" + /// A file type. case file = "File" + /// A geoPoint type. case geoPoint = "GeoPoint" + /// A polygon type. case polygon = "Polygon" + /// An array type. case array = "Array" + /// An object type. case object = "Object" + /// A pointer type. case pointer = "Pointer" + /// A relation type. case relation = "Relation" + /// A bytes type. case bytes = "Bytes" + /// A acl type. case acl = "ACL" } diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 642f12b01..9dcd6df22 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -12,10 +12,10 @@ import Foundation `ParseSchema` is used for handeling your schemas. - requires: `.useMasterKey` has to be available. */ -public struct ParseSchema: ParseSchemable { +public struct ParseSchema: ParseType, Decodable { /// The class name of the Schema. - var className: String + public var className: String /// The session token for this session. internal var fields: [String: ParseField]? @@ -25,16 +25,40 @@ public struct ParseSchema: ParseSchemable { /// The session token for this session. // internal var classLevelPermissions: [String: Codable]? + + /** + Get the current fields for this `ParseSchema`. + - returns: The current fields. + */ + public func getFields() -> [String: String] { + var currentFields = [String: String]() + fields?.forEach { (key, value) in + currentFields[key] = value.description + } + return currentFields + } + + /** + Get the current indexes for this `ParseSchema`. + - returns: The current indexes. + */ + public func getIndexes() -> [String: String] { + var currentIndexes = [String: String]() + indexes?.forEach { (key, value) in + currentIndexes[key] = value.description + } + return currentIndexes + } } // MARK: Default Implementations public extension ParseSchema { static var className: String { - T.className + SchemaObject.className } init() { - self.init(className: T.className) + self.init(className: SchemaObject.className) } /** From d4e3b8cc5e720c231b36b794dc5db44ce3b894e7 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 00:14:46 -0400 Subject: [PATCH 06/55] wip CLP --- ParseSwift.xcodeproj/project.pbxproj | 10 + .../ParseSwift/Protocols/ParseSchemable.swift | 2 +- Sources/ParseSwift/Types/ParseCLP.swift | 271 ++++++++++++++++++ 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 Sources/ParseSwift/Types/ParseCLP.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 5ba2258a7..5d0e31dd3 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -370,6 +370,10 @@ 709A149C2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; 709A149D2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; 709A149E2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; + 709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; + 709A14A12839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; + 709A14A22839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; + 709A14A32839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -1003,6 +1007,7 @@ 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A14952839B72300BF85E5 /* ParseSchemable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemable.swift; sourceTree = ""; }; 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchemable+async.swift"; sourceTree = ""; }; + 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1706,6 +1711,7 @@ 703B090126BD9652005A112F /* ParseAnalytics+async.swift */, 70170A482656E2FE0070C905 /* ParseAnalytics+combine.swift */, 91285B122698DBF20051B544 /* ParseBytes.swift */, + 709A149F2839CABD00BF85E5 /* ParseCLP.swift */, 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */, 703B090626BD9764005A112F /* ParseCloud+async.swift */, 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */, @@ -2274,6 +2280,7 @@ 7045769826BD917500F86F71 /* Query+async.swift in Sources */, 703B094E26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3825D998D90048EC1B /* ParseLDAP.swift in Sources */, + 709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */, 700395F225A171320052CB31 /* LiveQueryable.swift in Sources */, 70F03A252780BDF700E5AFB4 /* ParseGoogle+async.swift in Sources */, F97B45F224D9C6F200F4A88B /* Pointer.swift in Sources */, @@ -2515,6 +2522,7 @@ 703B094F26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3925D998D90048EC1B /* ParseLDAP.swift in Sources */, 700395F325A171320052CB31 /* LiveQueryable.swift in Sources */, + 709A14A12839CABD00BF85E5 /* ParseCLP.swift in Sources */, F97B45F324D9C6F200F4A88B /* Pointer.swift in Sources */, 703B095926BF480D005A112F /* ParseFacebook+combine.swift in Sources */, 70510AAD259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */, @@ -2859,6 +2867,7 @@ 7045769B26BD917500F86F71 /* Query+async.swift in Sources */, 703B095126BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3B25D998D90048EC1B /* ParseLDAP.swift in Sources */, + 709A14A32839CABD00BF85E5 /* ParseCLP.swift in Sources */, 700395F525A171320052CB31 /* LiveQueryable.swift in Sources */, F97B45FD24D9C6F200F4A88B /* ParseACL.swift in Sources */, 703B095B26BF480D005A112F /* ParseFacebook+combine.swift in Sources */, @@ -3006,6 +3015,7 @@ 7045769A26BD917500F86F71 /* Query+async.swift in Sources */, 703B095026BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3A25D998D90048EC1B /* ParseLDAP.swift in Sources */, + 709A14A22839CABD00BF85E5 /* ParseCLP.swift in Sources */, 700395F425A171320052CB31 /* LiveQueryable.swift in Sources */, F97B45FC24D9C6F200F4A88B /* ParseACL.swift in Sources */, 703B095A26BF480D005A112F /* ParseFacebook+combine.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseSchemable.swift b/Sources/ParseSwift/Protocols/ParseSchemable.swift index b72ef55ee..c7dd74924 100644 --- a/Sources/ParseSwift/Protocols/ParseSchemable.swift +++ b/Sources/ParseSwift/Protocols/ParseSchemable.swift @@ -9,7 +9,7 @@ import Foundation protocol ParseSchemable: ParseType, Decodable { - + /// The class name of the Schema. var className: String { get set } diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift new file mode 100644 index 000000000..9fe0cbc4e --- /dev/null +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -0,0 +1,271 @@ +// +// ParseCLP.swift +// ParseSwift +// +// Created by Corey Baker on 5/21/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +public struct ParseCLP: Codable, Equatable { + + var get: [String: Bool]? + var find: [String: Bool]? + var count: [String: Bool]? + var create: [String: Bool]? + var update: [String: Bool]? + var delete: [String: Bool]? + var addField: [String: Bool]? + var protectedFields: [String: [String]]? + var readUserFields: [String]? + var writeUserFields: [String]? + + enum Access: String, Codable { + case requiresAuthentication + case publicScope = "*" + } + + /* + func setAccess(_ key: WritableKeyPath, + userObjectId: String, allowed: Bool) -> Self where W: Codable { + var mutableCLP = self + mutableCLP[keyPath: key] = [userObjectId: allowed] + return mutableCLP + } */ +} + +func toRole(role: R) throws -> String where R: ParseRole { + guard let name = role.name else { + throw ParseError(code: .unknownError, message: "Name of ParseRole cannot be nil") + } + return "role:\(name)" +} + +// MARK: Protected +public extension ParseCLP { + + internal func getProtected(access: String) -> [String] { + protectedFields?[access] ?? [] + } + + /** + Get the protected fields for the given user objectId. + + - parameter userObjectId: The user objectId access to check. + - returns: The protected fields. + */ + func getProtected(_ userObjectId: String) -> [String] { + getProtected(access: userObjectId) + } + + /** + Get the protected fields for the given user objectId. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + */ + func getProtected(_ user: U) throws -> [String] where U: ParseUser { + let userPointer = try user.toPointer() + return getProtected(access: userPointer.objectId) + } + + /** + Get the protected fields for the given user objectId. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + */ + func getProtected(_ role: R) throws -> [String] where R: ParseRole { + let roleAccess = try toRole(role: role) + return getProtected(access: roleAccess) + } + + internal func setProtected(_ fields: [String], access: String) -> Self { + var mutableCLP = self + mutableCLP.protectedFields = [access: fields] + return mutableCLP + } + + /** + Set whether the given user objectId is allowed to retrieve fields from this class. + + - parameter userObjectId: The user objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtected(_ fields: [String], userObjectId: String) -> Self { + setProtected(fields, access: userObjectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter user: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtected(_ fields: [String], user: U) throws -> Self where U: ParseUser { + let userPointer = try user.toPointer() + return setProtected(fields, access: userPointer.objectId) + } + + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter user: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtected(_ fields: [String], role: R) throws -> Self where R: ParseRole { + let roleAccess = try toRole(role: role) + return setProtected(fields, access: roleAccess) + } +} + +// MARK: WriteUserFields +public extension ParseCLP { + + /** + Sets permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteUser(_ fields: [String]) -> Self { + var mutableCLP = self + mutableCLP.writeUserFields = fields + return mutableCLP + } +} + +// MARK: ReadUserFields +public extension ParseCLP { + + /** + Sets permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadUser(_ fields: [String]) -> Self { + var mutableCLP = self + mutableCLP.readUserFields = fields + return mutableCLP + } +} + +// MARK: WriteAccess +public extension ParseCLP { + + internal func setWrite(_ allowed: Bool, access: String) -> Self { + var mutableCLP = self + mutableCLP.get = [access: allowed] + mutableCLP.update = [access: allowed] + mutableCLP.delete = [access: allowed] + mutableCLP.addField = [access: allowed] + return mutableCLP + } + + /** + Sets whether the public is allowed to write this class. + + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setPublicWriteAccess(_ allowed: Bool) -> Self { + setWrite(allowed, access: Access.publicScope.rawValue) + } + + /** + Sets whether the given user objectId is allowed to write to this class. + + - parameter userObjectId: The user objectId to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteAccess(_ allowed: Bool, userObjectId: String) -> Self { + setWrite(allowed, access: userObjectId) + } + + /** + Sets whether the given `ParseUser` is allowed to write to this class. + + - parameter role: The `ParseUser` to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteAccess(_ allowed: Bool, user: U) throws -> Self where U: ParseUser { + let userPointer = try user.toPointer() + return setWrite(allowed, access: userPointer.objectId) + } + + /** + Sets whether the given `ParseRole` is allowed to write to this class. + + - parameter role: The `ParseRole` to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteAccess(_ allowed: Bool, role: R) throws -> Self where R: ParseRole { + let roleAccess = try toRole(role: role) + return setWrite(allowed, access: roleAccess) + } +} + +// MARK: ReadAccess +public extension ParseCLP { + + internal func setRead(_ allowed: Bool, access: String) -> Self { + var mutableCLP = self + mutableCLP.get = [access: allowed] + mutableCLP.find = [access: allowed] + mutableCLP.count = [access: allowed] + return mutableCLP + } + + /** + Sets whether the public is allowed to read this class. + + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setPublicReadAccess(_ allowed: Bool) -> Self { + setRead(allowed, access: Access.publicScope.rawValue) + } + + /** + Sets whether the given user objectId is allowed to read this class. + + - parameter userObjectId: The user objectId to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadAccess(_ allowed: Bool, userObjectId: String) -> Self { + setRead(allowed, access: userObjectId) + } + + /** + Sets whether the given `ParseUser` is allowed to read this class. + + - parameter role: The `ParseUser` to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadAccess(_ allowed: Bool, user: U) throws -> Self where U: ParseUser { + let userPointer = try user.toPointer() + return setRead(allowed, access: userPointer.objectId) + } + + /** + Sets whether the given `ParseRole` is allowed to read this class. + + - parameter role: The `ParseRole` to provide/restrict access to. + - parameter allowed: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadAccess(_ allowed: Bool, role: R) throws -> Self where R: ParseRole { + let roleAccess = try toRole(role: role) + return setRead(allowed, access: roleAccess) + } +} From 3e8d4d3fe7aedc965fff15cc2022691f4c3c944c Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 12:04:58 -0400 Subject: [PATCH 07/55] adding read checks --- Sources/ParseSwift/Types/ParseACL.swift | 31 +- Sources/ParseSwift/Types/ParseCLP.swift | 631 ++++++++++++++++++++---- 2 files changed, 547 insertions(+), 115 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseACL.swift b/Sources/ParseSwift/Types/ParseACL.swift index 61ece4801..7d7902b9f 100644 --- a/Sources/ParseSwift/Types/ParseACL.swift +++ b/Sources/ParseSwift/Types/ParseACL.swift @@ -47,6 +47,17 @@ public struct ParseACL: ParseType, /// The default initializer. public init() { } + static func getRoleAccessName(_ role: R) throws -> String where R: ParseRole { + guard let name = role.name else { + throw ParseError(code: .unknownError, message: "Name of ParseRole cannot be nil") + } + return getRoleAccessName(name) + } + + static func getRoleAccessName(_ name: String) -> String { + return "role:\(name)" + } + /** Controls whether the public is allowed to read this object. */ @@ -195,7 +206,7 @@ public struct ParseACL: ParseType, - returns: `true` if the role has read access, otherwise **false**. */ public func getReadAccess(roleName: String) -> Bool { - get(toRole(roleName: roleName), access: .read) + get(Self.getRoleAccessName(roleName), access: .read) } /** @@ -207,7 +218,7 @@ public struct ParseACL: ParseType, */ public func getReadAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } - return get(toRole(roleName: name), access: .read) + return get(Self.getRoleAccessName(name), access: .read) } /** @@ -218,7 +229,7 @@ public struct ParseACL: ParseType, - returns: `true` if the role has read access, otherwise **false**. */ public func getWriteAccess(roleName: String) -> Bool { - get(toRole(roleName: roleName), access: .write) + get(Self.getRoleAccessName(roleName), access: .write) } /** @@ -230,7 +241,7 @@ public struct ParseACL: ParseType, */ public func getWriteAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } - return get(toRole(roleName: name), access: .write) + return get(Self.getRoleAccessName(name), access: .write) } /** @@ -240,7 +251,7 @@ public struct ParseACL: ParseType, - parameter roleName: The name of the role. */ public mutating func setReadAccess(roleName: String, value: Bool) { - set(toRole(roleName: roleName), access: .read, value: value) + set(Self.getRoleAccessName(roleName), access: .read, value: value) } /** @@ -251,7 +262,7 @@ public struct ParseACL: ParseType, */ public mutating func setReadAccess(role: T, value: Bool) where T: ParseRole { guard let name = role.name else { return } - set(toRole(roleName: name), access: .read, value: value) + set(Self.getRoleAccessName(name), access: .read, value: value) } /** @@ -261,7 +272,7 @@ public struct ParseACL: ParseType, - parameter roleName: The name of the role. */ public mutating func setWriteAccess(roleName: String, value: Bool) { - set(toRole(roleName: roleName), access: .write, value: value) + set(Self.getRoleAccessName(roleName), access: .write, value: value) } /** @@ -272,11 +283,7 @@ public struct ParseACL: ParseType, */ public mutating func setWriteAccess(role: T, value: Bool) where T: ParseRole { guard let name = role.name else { return } - set(toRole(roleName: name), access: .write, value: value) - } - - private func toRole(roleName: String) -> String { - "role:\(roleName)" + set(Self.getRoleAccessName(name), access: .write, value: value) } private mutating func set(_ key: String, access: Access, value: Bool) { diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 9fe0cbc4e..786ff912b 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -10,53 +10,71 @@ import Foundation public struct ParseCLP: Codable, Equatable { - var get: [String: Bool]? - var find: [String: Bool]? - var count: [String: Bool]? - var create: [String: Bool]? - var update: [String: Bool]? - var delete: [String: Bool]? - var addField: [String: Bool]? - var protectedFields: [String: [String]]? - var readUserFields: [String]? - var writeUserFields: [String]? + public internal(set) var get: [String: Bool]? + public internal(set) var find: [String: Bool]? + public internal(set) var count: [String: Bool]? + public internal(set) var create: [String: Bool]? + public internal(set) var update: [String: Bool]? + public internal(set) var delete: [String: Bool]? + public internal(set) var addField: [String: Bool]? + public internal(set) var protectedFields: [String: [String]]? + public internal(set) var readUserFields: [String]? + public internal(set) var writeUserFields: [String]? enum Access: String, Codable { case requiresAuthentication case publicScope = "*" } - /* - func setAccess(_ key: WritableKeyPath, - userObjectId: String, allowed: Bool) -> Self where W: Codable { + func getAccess(_ key: KeyPath, + for entity: String) -> Bool { + self[keyPath: key]?[entity] ?? false + } + + func setAccess(_ key: WritableKeyPath, + for entity: String, + to allow: Bool) -> Self { var mutableCLP = self - mutableCLP[keyPath: key] = [userObjectId: allowed] + mutableCLP[keyPath: key]?[entity] = allow return mutableCLP - } */ -} + } -func toRole(role: R) throws -> String where R: ParseRole { - guard let name = role.name else { - throw ParseError(code: .unknownError, message: "Name of ParseRole cannot be nil") + func getUser(_ key: KeyPath) -> [String] { + self[keyPath: key] ?? [] + } + + func setUser(_ key: WritableKeyPath, + fields: [String]) -> Self { + var mutableCLP = self + mutableCLP[keyPath: key] = fields + return mutableCLP + } + + func getProtected(_ key: KeyPath, + for entity: String) -> [String] { + self[keyPath: key]?[entity] ?? [] + } + + func setProtected(_ key: WritableKeyPath, + fields: [String], + for entity: String) -> Self { + var mutableCLP = self + mutableCLP[keyPath: key]?[entity] = fields + return mutableCLP } - return "role:\(name)" } // MARK: Protected public extension ParseCLP { - internal func getProtected(access: String) -> [String] { - protectedFields?[access] ?? [] - } - /** Get the protected fields for the given user objectId. - - parameter userObjectId: The user objectId access to check. + - parameter objectId: The user objectId access to check. - returns: The protected fields. */ - func getProtected(_ userObjectId: String) -> [String] { - getProtected(access: userObjectId) + func getProtected(_ objectId: String) -> [String] { + getProtected(\.protectedFields, for: objectId) } /** @@ -66,36 +84,30 @@ public extension ParseCLP { - returns: The protected fields. */ func getProtected(_ user: U) throws -> [String] where U: ParseUser { - let userPointer = try user.toPointer() - return getProtected(access: userPointer.objectId) + let objectId = try user.toPointer().objectId + return getProtected(\.protectedFields, for: objectId) } /** Get the protected fields for the given user objectId. - - parameter user: The `ParseUser` access to check. + - parameter role: The `ParseRole` access to check. - returns: The protected fields. */ func getProtected(_ role: R) throws -> [String] where R: ParseRole { - let roleAccess = try toRole(role: role) - return getProtected(access: roleAccess) - } - - internal func setProtected(_ fields: [String], access: String) -> Self { - var mutableCLP = self - mutableCLP.protectedFields = [access: fields] - return mutableCLP + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return getProtected(\.protectedFields, for: roleNameAccess) } /** Set whether the given user objectId is allowed to retrieve fields from this class. - - parameter userObjectId: The user objectId to provide/restrict access to. + - parameter objectId: The user objectId to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtected(_ fields: [String], userObjectId: String) -> Self { - setProtected(fields, access: userObjectId) + func setProtected(_ fields: [String], objectId: String) -> Self { + setProtected(\.protectedFields, fields: fields, for: objectId) } /** @@ -106,8 +118,8 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setProtected(_ fields: [String], user: U) throws -> Self where U: ParseUser { - let userPointer = try user.toPointer() - return setProtected(fields, access: userPointer.objectId) + let objectId = try user.toPointer().objectId + return setProtected(\.protectedFields, fields: fields, for: objectId) } /** @@ -118,14 +130,23 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setProtected(_ fields: [String], role: R) throws -> Self where R: ParseRole { - let roleAccess = try toRole(role: role) - return setProtected(fields, access: roleAccess) + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setProtected(\.protectedFields, fields: fields, for: roleNameAccess) } } // MARK: WriteUserFields public extension ParseCLP { + /** + Get the `writeUserFields`. + + - returns: User pointer fields. + */ + func getWriteUserFields() -> [String] { + getUser(\.writeUserFields) + } + /** Sets permission for the user pointer fields or create/delete/update/addField operations. @@ -133,15 +154,22 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteUser(_ fields: [String]) -> Self { - var mutableCLP = self - mutableCLP.writeUserFields = fields - return mutableCLP + setUser(\.writeUserFields, fields: fields) } } // MARK: ReadUserFields public extension ParseCLP { + /** + Get the `readUserFields`. + + - returns: User pointer fields. + */ + func getReadUserFields() -> [String] { + getUser(\.readUserFields) + } + /** Sets permission for the user pointer fields or get/count/find operations. @@ -149,123 +177,520 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadUser(_ fields: [String]) -> Self { - var mutableCLP = self - mutableCLP.readUserFields = fields - return mutableCLP + setUser(\.readUserFields, fields: fields) + } +} + +// MARK: RequiresAuthenication +public extension ParseCLP { + + /** + Check whether **get** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesGetRequireAuthentication() -> Bool { + getAccess(\.get, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **find** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesFindRequireAuthentication() -> Bool { + getAccess(\.find, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **count** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesCountRequireAuthentication() -> Bool { + getAccess(\.count, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **create** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesCreateRequireAuthentication() -> Bool { + getAccess(\.create, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **update** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesUpdateRequireAuthentication() -> Bool { + getAccess(\.update, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **delete** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesDeleteRequireAuthentication() -> Bool { + getAccess(\.delete, for: Access.requiresAuthentication.rawValue) + } + + /** + Check whether **addField** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesAddFieldRequireAuthentication() -> Bool { + getAccess(\.addField, for: Access.requiresAuthentication.rawValue) + } + + /** + Sets whether **get** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setGetRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.get, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **find** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setFindRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.find, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **count** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setCountRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.count, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **create** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setCreateRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.create, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **update** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setUpdateRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.update, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **delete** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setDeleteRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.delete, for: Access.requiresAuthentication.rawValue, to: allow) + } + + /** + Sets whether **addField** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setAddFieldRequiresAuthentication(_ allow: Bool) -> Self { + setAccess(\.addField, for: Access.requiresAuthentication.rawValue, to: allow) + } +} + +// MARK: PublicAccess +public extension ParseCLP { + + /** + Check whether **get** has public access for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesGetHavePublicAccess() -> Bool { + getAccess(\.get, for: Access.publicScope.rawValue) + } + + /** + Check whether **find** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesFindHavePublicAccess() -> Bool { + getAccess(\.find, for: Access.publicScope.rawValue) + } + + /** + Check whether **count** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesCountHavePublicAccess() -> Bool { + getAccess(\.count, for: Access.publicScope.rawValue) + } + + /** + Check whether **create** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesCreateHavePublicAccess() -> Bool { + getAccess(\.create, for: Access.publicScope.rawValue) + } + + /** + Check whether **update** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesUpdateHavePublicAccess() -> Bool { + getAccess(\.update, for: Access.publicScope.rawValue) + } + + /** + Check whether **delete** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesDeleteHavePublicAccess() -> Bool { + getAccess(\.delete, for: Access.publicScope.rawValue) + } + + /** + Check whether **addField** requires authentication for this class. + + - returns: **true** if access is allowed, **false** otherwise. + */ + func doesAddFieldHavePublicAccess() -> Bool { + getAccess(\.addField, for: Access.publicScope.rawValue) + } + + /** + Sets whether **get** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setGetPublicAccess(_ allow: Bool) -> Self { + setAccess(\.get, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **find** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setFindPublicAccess(_ allow: Bool) -> Self { + setAccess(\.find, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **count** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setCountPublicAccess(_ allow: Bool) -> Self { + setAccess(\.count, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **create** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setCreatePublicAccess(_ allow: Bool) -> Self { + setAccess(\.create, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **update** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setUpdatePublicAccess(_ allow: Bool) -> Self { + setAccess(\.update, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **delete** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setDeletePublicAccess(_ allow: Bool) -> Self { + setAccess(\.delete, for: Access.publicScope.rawValue, to: allow) + } + + /** + Sets whether **addField** requires authentication for this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setAddFieldPublicAccess(_ allow: Bool) -> Self { + setAccess(\.addField, for: Access.publicScope.rawValue, to: allow) } } // MARK: WriteAccess public extension ParseCLP { - internal func setWrite(_ allowed: Bool, access: String) -> Self { - var mutableCLP = self - mutableCLP.get = [access: allowed] - mutableCLP.update = [access: allowed] - mutableCLP.delete = [access: allowed] - mutableCLP.addField = [access: allowed] - return mutableCLP + internal func setWrite(_ entity: String, + to allow: Bool, + can addField: Bool) -> Self { + var updatedCLP = self + .setAccess(\.create, for: entity, to: allow) + .setAccess(\.update, for: entity, to: allow) + .setAccess(\.delete, for: entity, to: allow) + if addField { + updatedCLP = updatedCLP.setAccess(\.addField, for: entity, to: allow) + } + return updatedCLP } + func getWrite(_ entity: String, check addField: Bool) -> Bool { + let access = getAccess(\.create, for: entity) + && getAccess(\.update, for: entity) + && getAccess(\.delete, for: entity) + if addField { + return access && getAccess(\.addField, for: entity) + } + return access + } + /** - Sets whether the public is allowed to write this class. + Check whether authentication is required to write to this class. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesWriteRequireAuthentication(_ checkAddField: Bool = false) -> Bool { + getWrite(Access.requiresAuthentication.rawValue, check: checkAddField) + } - - parameter allowed: **true** if access should be allowed, **false** otherwise. + /** + Check whether the public has access to write to this class. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesWriteHavePublicAccess(_ checkAddField: Bool = false) -> Bool { + getWrite(Access.publicScope.rawValue, check: checkAddField) + } + + /** + Check whether the user objectId has access to write to this class. + - parameter objectId: The user objectId to check. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesHaveWriteAccess(_ objectId: String, + checkAddField: Bool = false) -> Bool { + getWrite(objectId, check: checkAddField) + } + + /** + Check whether the `ParseUser` pointer has access to write to this class. + - parameter user: The `ParseUser` pointer to check. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesHaveWriteAccess(_ user: Pointer, + checkAddField: Bool = false) -> Bool where U: ParseUser { + doesHaveWriteAccess(user.objectId, checkAddField: checkAddField) + } + + /** + Check whether the `ParseUser` has access to write to this class. + - parameter user: The `ParseUser` to check. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesHaveWriteAccess(_ user: U, + checkAddField: Bool = false) throws -> Bool where U: ParseUser { + let objectId = try user.toPointer().objectId + return doesHaveWriteAccess(objectId, checkAddField: checkAddField) + } + + /** + Check whether the `ParseRole` has access to write to this class. + - parameter role: The `ParseRole` to check. + - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. + Defaults to **false**. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesHaveWriteAccess(_ role: R, + checkAddField: Bool = false) throws -> Bool where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return doesHaveWriteAccess(roleNameAccess, checkAddField: checkAddField) + } + + /** + Sets whether authentication is required to create/update/delete/addField this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setPublicWriteAccess(_ allowed: Bool) -> Self { - setWrite(allowed, access: Access.publicScope.rawValue) + func setRequiresAuthenticationWriteAccess(_ allow: Bool, + canAddField addField: Bool = false) -> Self { + setWrite(Access.requiresAuthentication.rawValue, to: allow, can: addField) } /** - Sets whether the given user objectId is allowed to write to this class. + Sets whether the public is allowed to create/update/delete/addField this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setPublicWriteAccess(_ allow: Bool, + canAddField addField: Bool = false) -> Self { + setWrite(Access.publicScope.rawValue, to: allow, can: addField) + } + + /** + Sets whether the given user objectId is allowed to create/update/delete/addField to this class. - - parameter userObjectId: The user objectId to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter objectId: The user objectId to provide/restrict access to. + - parameter to: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteAccess(_ allowed: Bool, userObjectId: String) -> Self { - setWrite(allowed, access: userObjectId) + func setWriteAccess(_ objectId: String, + to allow: Bool, + canAddField addField: Bool = false) -> Self { + setWrite(objectId, to: allow, can: addField) } /** - Sets whether the given `ParseUser` is allowed to write to this class. + Sets whether the given `ParseUser` is allowed to create/update/delete/addField to this class. - - parameter role: The `ParseUser` to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter user: The `ParseUser` to provide/restrict access to. + - parameter to: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteAccess(_ allowed: Bool, user: U) throws -> Self where U: ParseUser { - let userPointer = try user.toPointer() - return setWrite(allowed, access: userPointer.objectId) + func setWriteAccess(_ user: U, + to allow: Bool, + canAddField addField: Bool = false) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setWrite(objectId, to: allow, can: addField) } /** - Sets whether the given `ParseRole` is allowed to write to this class. + Sets whether the given `ParseRole` is allowed to create/update/delete/addField to this class. - parameter role: The `ParseRole` to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter to: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteAccess(_ allowed: Bool, role: R) throws -> Self where R: ParseRole { - let roleAccess = try toRole(role: role) - return setWrite(allowed, access: roleAccess) + func setWriteAccess(_ role: R, + to allow: Bool, + canAddField addField: Bool = false) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setWrite(roleNameAccess, to: allow, can: addField) } } // MARK: ReadAccess public extension ParseCLP { - internal func setRead(_ allowed: Bool, access: String) -> Self { - var mutableCLP = self - mutableCLP.get = [access: allowed] - mutableCLP.find = [access: allowed] - mutableCLP.count = [access: allowed] - return mutableCLP + internal func setRead(_ entity: String, to allow: Bool) -> Self { + let updatedCLP = self + .setAccess(\.get, for: entity, to: allow) + .setAccess(\.find, for: entity, to: allow) + .setAccess(\.count, for: entity, to: allow) + return updatedCLP + } + + /** + Sets whether authentication is required to get/find/count this class. + + - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setRequiresAuthenticationReadAccess(_ allow: Bool, + canAddField addField: Bool = false) -> Self { + setRead(Access.requiresAuthentication.rawValue, to: allow) } /** - Sets whether the public is allowed to read this class. + Sets whether the public is allowed to get/find/count this class. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setPublicReadAccess(_ allowed: Bool) -> Self { - setRead(allowed, access: Access.publicScope.rawValue) + func setPublicReadAccess(_ allow: Bool) -> Self { + setRead(Access.publicScope.rawValue, to: allow) } /** - Sets whether the given user objectId is allowed to read this class. + Sets whether the given user objectId is allowed to get/find/count this class. - - parameter userObjectId: The user objectId to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter objectId: The user objectId to provide/restrict access to. + - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadAccess(_ allowed: Bool, userObjectId: String) -> Self { - setRead(allowed, access: userObjectId) + func setReadAccess(_ objectId: String, to allow: Bool) -> Self { + setRead(objectId, to: allow) } /** - Sets whether the given `ParseUser` is allowed to read this class. + Sets whether the given `ParseUser` is allowed to get/find/count this class. - parameter role: The `ParseUser` to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadAccess(_ allowed: Bool, user: U) throws -> Self where U: ParseUser { - let userPointer = try user.toPointer() - return setRead(allowed, access: userPointer.objectId) + func setReadAccess(_ user: U, to allow: Bool) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setRead(objectId, to: allow) } /** - Sets whether the given `ParseRole` is allowed to read this class. + Sets whether the given `ParseRole` is allowed to get/find/count this class. - parameter role: The `ParseRole` to provide/restrict access to. - - parameter allowed: **true** if access should be allowed, **false** otherwise. + - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadAccess(_ allowed: Bool, role: R) throws -> Self where R: ParseRole { - let roleAccess = try toRole(role: role) - return setRead(allowed, access: roleAccess) + func setReadAccess(_ role: R, to allow: Bool) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setRead(roleNameAccess, to: allow) } } From b28010da2106e4b228a9999606b4cf65865028ec Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 12:39:53 -0400 Subject: [PATCH 08/55] finished CLP --- Sources/ParseSwift/Types/ParseCLP.swift | 160 +++++++++++++++++++++--- 1 file changed, 142 insertions(+), 18 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 786ff912b..b0dafd7e7 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -78,60 +78,81 @@ public extension ParseCLP { } /** - Get the protected fields for the given user objectId. + Get the protected fields for the given `ParseUser`. - parameter user: The `ParseUser` access to check. - returns: The protected fields. */ func getProtected(_ user: U) throws -> [String] where U: ParseUser { let objectId = try user.toPointer().objectId - return getProtected(\.protectedFields, for: objectId) + return getProtected(objectId) } /** - Get the protected fields for the given user objectId. + Get the protected fields for the given `ParseUser` pointer. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + */ + func getProtected(_ user: Pointer) -> [String] where U: ParseUser { + getProtected(user.objectId) + } + + /** + Get the protected fields for the given `ParseRole`. - parameter role: The `ParseRole` access to check. - returns: The protected fields. */ func getProtected(_ role: R) throws -> [String] where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return getProtected(\.protectedFields, for: roleNameAccess) + return getProtected(roleNameAccess) } /** Set whether the given user objectId is allowed to retrieve fields from this class. - - parameter objectId: The user objectId to provide/restrict access to. + - parameter for: The user objectId to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtected(_ fields: [String], objectId: String) -> Self { + func setProtected(_ fields: [String], for objectId: String) -> Self { setProtected(\.protectedFields, fields: fields, for: objectId) } /** Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - parameter user: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtected(_ fields: [String], user: U) throws -> Self where U: ParseUser { + func setProtected(_ fields: [String], for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setProtected(\.protectedFields, fields: fields, for: objectId) + return setProtected(fields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtected(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { + setProtected(fields, for: user.objectId) } /** Set whether the given `ParseRole` is allowed to retrieve fields from this class. - - parameter user: The `ParseRole` to provide/restrict access to. + - parameter for: The `ParseRole` to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtected(_ fields: [String], role: R) throws -> Self where R: ParseRole { + func setProtected(_ fields: [String], for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setProtected(\.protectedFields, fields: fields, for: roleNameAccess) + return setProtected(fields, for: roleNameAccess) } } @@ -471,7 +492,7 @@ public extension ParseCLP { return updatedCLP } - func getWrite(_ entity: String, check addField: Bool) -> Bool { + internal func getWrite(_ entity: String, check addField: Bool) -> Bool { let access = getAccess(\.create, for: entity) && getAccess(\.update, for: entity) && getAccess(\.delete, for: entity) @@ -480,7 +501,7 @@ public extension ParseCLP { } return access } - + /** Check whether authentication is required to write to this class. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. @@ -507,6 +528,8 @@ public extension ParseCLP { - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. */ func doesHaveWriteAccess(_ objectId: String, checkAddField: Bool = false) -> Bool { @@ -519,6 +542,8 @@ public extension ParseCLP { - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. */ func doesHaveWriteAccess(_ user: Pointer, checkAddField: Bool = false) -> Bool where U: ParseUser { @@ -531,6 +556,8 @@ public extension ParseCLP { - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. */ func doesHaveWriteAccess(_ user: U, checkAddField: Bool = false) throws -> Bool where U: ParseUser { @@ -544,6 +571,8 @@ public extension ParseCLP { - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. */ func doesHaveWriteAccess(_ role: R, checkAddField: Bool = false) throws -> Bool where R: ParseRole { @@ -605,7 +634,22 @@ public extension ParseCLP { to allow: Bool, canAddField addField: Bool = false) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setWrite(objectId, to: allow, can: addField) + return setWriteAccess(objectId, to: allow, canAddField: addField) + } + + /** + Sets whether the given `ParseUser`pointer is allowed to create/update/delete/addField to this class. + + - parameter user: The `ParseUser` to provide/restrict access to. + - parameter to: **true** if access should be allowed, **false** otherwise. + - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. + Defaults to **false**. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteAccess(_ user: Pointer, + to allow: Bool, + canAddField addField: Bool = false) -> Self where U: ParseUser { + setWriteAccess(user.objectId, to: allow, canAddField: addField) } /** @@ -621,7 +665,7 @@ public extension ParseCLP { to allow: Bool, canAddField addField: Bool = false) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setWrite(roleNameAccess, to: allow, can: addField) + return setWriteAccess(roleNameAccess, to: allow, canAddField: addField) } } @@ -636,6 +680,74 @@ public extension ParseCLP { return updatedCLP } + internal func getRead(_ entity: String) -> Bool { + getAccess(\.get, for: entity) + && getAccess(\.find, for: entity) + && getAccess(\.count, for: entity) + } + + /** + Check whether authentication is required to read from this class. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesReadRequireAuthentication() -> Bool { + getRead(Access.requiresAuthentication.rawValue) + } + + /** + Check whether the public has access to read from this class. + - returns: **true** if authentication is required, **false** otherwise. + */ + func doesReadHavePublicAccess() -> Bool { + getRead(Access.publicScope.rawValue) + } + + /** + Check whether the user objectId has access to read from this class. + - parameter objectId: The user objectId to check. + - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. + */ + func doesHaveReadAccess(_ objectId: String) -> Bool { + getRead(objectId) + } + + /** + Check whether the `ParseUser` pointer has access to read from this class. + - parameter user: The `ParseUser` pointer to check. + - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. + */ + func doesHaveReadAccess(_ user: Pointer) -> Bool where U: ParseUser { + doesHaveReadAccess(user.objectId) + } + + /** + Check whether the `ParseUser` has access to read from this class. + - parameter user: The `ParseUser` to check. + - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. + */ + func doesHaveReadAccess(_ user: U) throws -> Bool where U: ParseUser { + let objectId = try user.toPointer().objectId + return doesHaveReadAccess(objectId) + } + + /** + Check whether the `ParseRole` has access to read from this class. + - parameter role: The `ParseRole` to check. + - returns: **true** if authentication is required, **false** otherwise. + - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still + have access if they aare apart of a `ParseRole` that has access. + */ + func doesHaveReadAccess(_ role: R) throws -> Bool where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return doesHaveReadAccess(roleNameAccess) + } + /** Sets whether authentication is required to get/find/count this class. @@ -679,7 +791,19 @@ public extension ParseCLP { */ func setReadAccess(_ user: U, to allow: Bool) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setRead(objectId, to: allow) + return setReadAccess(objectId, to: allow) + } + + /** + Sets whether the given `ParseUser` is allowed to get/find/count this class. + + - parameter role: The `ParseUser` pointer to provide/restrict access to. + - parameter allow: **true** if access should be allowed, **false** otherwise. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadAccess(_ user: Pointer, + to allow: Bool) -> Self where U: ParseUser { + return setReadAccess(user.objectId, to: allow) } /** @@ -691,6 +815,6 @@ public extension ParseCLP { */ func setReadAccess(_ role: R, to allow: Bool) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setRead(roleNameAccess, to: allow) + return setReadAccess(roleNameAccess, to: allow) } } From f6c645b1d781464682fbf7e8be027915a9de25de Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 13:06:34 -0400 Subject: [PATCH 09/55] add inits to CLP --- Sources/ParseSwift/Types/ParseCLP.swift | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index b0dafd7e7..c813686a5 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -26,6 +26,9 @@ public struct ParseCLP: Codable, Equatable { case publicScope = "*" } + /// An empty CLP. + public init() { } + func getAccess(_ key: KeyPath, for entity: String) -> Bool { self[keyPath: key]?[entity] ?? false @@ -64,6 +67,40 @@ public struct ParseCLP: Codable, Equatable { } } +// MARK: Default Implementation +public extension ParseCLP { + + init(requireAuthentication: Bool, publicAccess: Bool) { + let clp = setRequiresAuthenticationWriteAccess(requireAuthentication) + .setRequiresAuthenticationReadAccess(requireAuthentication) + .setPublicWriteAccess(publicAccess) + .setPublicReadAccess(publicAccess) + self = clp + } + + init(objectId: String, canAddFied: Bool = false) { + let clp = setWriteAccess(objectId, + to: true, + canAddField: canAddFied) + .setReadAccess(objectId, to: true) + self = clp + } + + init(user: U, canAddFied: Bool = false) throws where U: ParseUser { + let objectId = try user.toPointer().objectId + self.init(objectId: objectId, canAddFied: canAddFied) + } + + init(user: Pointer, canAddFied: Bool = false) where U: ParseUser { + self.init(objectId: user.objectId, canAddFied: canAddFied) + } + + init(role: R, canAddFied: Bool = false) throws where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + self.init(objectId: roleNameAccess, canAddFied: canAddFied) + } +} + // MARK: Protected public extension ParseCLP { From f71fe1646cea52a9518e95944282631113d1b528 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 13:19:39 -0400 Subject: [PATCH 10/55] add more inits --- .../20 - Schema.xcplaygroundpage/Contents.swift | 6 ++++-- Sources/ParseSwift/Types/ParseSchema.swift | 15 ++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 36a3d691a..bba4c1f17 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -43,6 +43,8 @@ extension GameScore { } } -let schema = ParseSchema() -schema. +let schema = ParseSchema(classLevelPermissions: .init()) + +print(schema.className) + //: [Next](@next) diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 9dcd6df22..db7da6bf0 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -14,17 +14,17 @@ import Foundation */ public struct ParseSchema: ParseType, Decodable { - /// The class name of the Schema. + /// The class name of the `ParseSchema`. public var className: String - /// The session token for this session. + /// The fiekds of this `ParseSchema`. internal var fields: [String: ParseField]? - /// The session token for this session. + /// The indexs of this `ParseSchema`. internal var indexes: [String: AnyCodable]? - /// The session token for this session. - // internal var classLevelPermissions: [String: Codable]? + /// The CLPs of this `ParseSchema`. + public var classLevelPermissions: ParseCLP? /** Get the current fields for this `ParseSchema`. @@ -61,6 +61,11 @@ public extension ParseSchema { self.init(className: SchemaObject.className) } + init(classLevelPermissions: ParseCLP) { + self.init(className: SchemaObject.className) + self.classLevelPermissions = classLevelPermissions + } + /** Add a Field to create/update a `ParseSchema`. From 33d589c44862b8a68c6984c1f7f0e8b35f205548 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 13:29:28 -0400 Subject: [PATCH 11/55] doc nits --- .../3rd Party/ParseApple/ParseApple.swift | 2 +- .../ParseFacebook/ParseFacebook.swift | 2 +- .../3rd Party/ParseGithub/ParseGitHub.swift | 2 +- .../3rd Party/ParseGoogle/ParseGoogle.swift | 2 +- .../3rd Party/ParseLDAP/ParseLDAP.swift | 2 +- .../ParseLinkedIn/ParseLinkedIn.swift | 2 +- .../3rd Party/ParseTwitter/ParseTwitter.swift | 2 +- .../Protocols/ParseAuthentication.swift | 6 ++-- Sources/ParseSwift/Coding/ParseEncoder.swift | 2 +- .../LiveQuery/ParseLiveQuery+async.swift | 2 +- .../LiveQuery/ParseLiveQuery+combine.swift | 2 +- .../ParseSwift/LiveQuery/ParseLiveQuery.swift | 2 +- Sources/ParseSwift/Objects/ParseObject.swift | 4 +-- Sources/ParseSwift/Parse.swift | 6 ++-- Sources/ParseSwift/Types/ParseACL.swift | 28 +++++++++---------- .../ParseSwift/Types/ParseConfig+async.swift | 2 +- Sources/ParseSwift/Types/ParseConfig.swift | 2 +- Sources/ParseSwift/Types/Pointer.swift | 6 ++-- .../ParseSwift/Types/QueryConstraint.swift | 6 ++-- Tests/ParseSwiftTests/ParseObjectTests.swift | 4 +-- 20 files changed, 43 insertions(+), 43 deletions(-) diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift index 3e960a8cb..1e678269a 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple.swift @@ -39,7 +39,7 @@ public struct ParseApple: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.token.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift index 6a7e720f8..4c03525f1 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook.swift @@ -53,7 +53,7 @@ public struct ParseFacebook: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil else { return false diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift index 659f3f416..57652b6e2 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGithub/ParseGitHub.swift @@ -38,7 +38,7 @@ public struct ParseGitHub: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.accessToken.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift index 272331b04..36dea317c 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseGoogle/ParseGoogle.swift @@ -43,7 +43,7 @@ public struct ParseGoogle: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil else { return false diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift index fbdf633cf..4cf83313c 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP.swift @@ -33,7 +33,7 @@ public struct ParseLDAP: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.password.rawValue] != nil else { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift index fba77c3ea..84d762b74 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLinkedIn/ParseLinkedIn.swift @@ -41,7 +41,7 @@ public struct ParseLinkedIn: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.accessToken.rawValue] != nil, diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift index bc68a3344..8f065e6a4 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift @@ -54,7 +54,7 @@ public struct ParseTwitter: ParseAuthentication { /// Verifies all mandatory keys are in authData. /// - parameter authData: Dictionary containing key/values. - /// - returns: `true` if all the mandatory keys are present, **false** otherwise. + /// - returns: **true** if all the mandatory keys are present, **false** otherwise. func verifyMandatoryKeys(authData: [String: String]) -> Bool { guard authData[AuthenticationKeys.id.rawValue] != nil, authData[AuthenticationKeys.consumerKey.rawValue] != nil, diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift index ee07bec16..385462581 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift @@ -25,7 +25,7 @@ public protocol ParseAuthentication: Codable { /// The type of authentication. static var __type: String { get } // swiftlint:disable:this identifier_name - /// Returns `true` if the *current* user is linked to the respective authentication type. + /// Returns **true** if the *current* user is linked to the respective authentication type. var isLinked: Bool { get } /// The default initializer for this authentication type. @@ -58,7 +58,7 @@ public protocol ParseAuthentication: Codable { /** Whether the `ParseUser` is logged in with the respective authentication type. - parameter user: The `ParseUser` to check authentication type. The user must be logged in on this device. - - returns: `true` if the `ParseUser` is logged in via the repective + - returns: **true** if the `ParseUser` is logged in via the repective authentication type. **false** if the user is not. */ func isLinked(with user: AuthenticatedUser) -> Bool @@ -306,7 +306,7 @@ public extension ParseUser { /** Whether the `ParseUser` is logged in with the respective authentication string type. - parameter type: The authentication type to check. The user must be logged in on this device. - - returns: `true` if the `ParseUser` is logged in via the repective + - returns: **true** if the `ParseUser` is logged in via the repective authentication type. **false** if the user is not. */ func isLinked(with type: String) -> Bool { diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift index 05710f6bd..2943deb9d 100644 --- a/Sources/ParseSwift/Coding/ParseEncoder.swift +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -233,7 +233,7 @@ internal class _ParseEncoder: JSONEncoder, Encoder { /// Returns whether a new element can be encoded at this coding path. /// - /// `true` if an element has not yet been encoded at this coding path; **false** otherwise. + /// **true** if an element has not yet been encoded at this coding path; **false** otherwise. var canEncodeNewValue: Bool { // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. diff --git a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift index e98191070..515f72359 100644 --- a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift +++ b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift @@ -14,7 +14,7 @@ extension ParseLiveQuery { /** Manually establish a connection to the `ParseLiveQuery` Server. - - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to `true`. + - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to **true**. - returns: An instance of the logged in `ParseUser`. - throws: An error of type `ParseError`. */ diff --git a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+combine.swift b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+combine.swift index 0e34cf452..c895067c5 100644 --- a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+combine.swift +++ b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+combine.swift @@ -15,7 +15,7 @@ extension ParseLiveQuery { /** Manually establish a connection to the `ParseLiveQuery` Server. Publishes when established. - - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to `true`. + - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to **true**. - returns: A publisher that eventually produces a single value and then finishes or fails. */ public func openPublisher(isUserWantsToConnect: Bool = true) -> Future { diff --git a/Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift b/Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift index 2cf4475c8..5fe754031 100644 --- a/Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift +++ b/Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift @@ -600,7 +600,7 @@ extension ParseLiveQuery: LiveQuerySocketDelegate { extension ParseLiveQuery { /// Manually establish a connection to the `ParseLiveQuery` Server. - /// - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to `true`. + /// - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to **true**. /// - parameter completion: Returns `nil` if successful, an `Error` otherwise. public func open(isUserWantsToConnect: Bool = true, completion: @escaping (Error?) -> Void) { synchronizationQueue.sync { diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 5942ab009..018a41999 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -85,7 +85,7 @@ public protocol ParseObject: Objectable, Determines if a `KeyPath` of the current `ParseObject` should be restored by comparing it to another `ParseObject`. - parameter original: The original `ParseObject`. - - returns: Returns a `true` if the keyPath should be restored or **false** otherwise. + - returns: Returns a **true** if the keyPath should be restored or **false** otherwise. */ func shouldRestoreKey(_ key: KeyPath, original: Self) -> Bool where W: Equatable @@ -169,7 +169,7 @@ public extension ParseObject { /** Determines if two objects have the same objectId. - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: T) -> Bool { return other.className == className && other.objectId == objectId && objectId != nil diff --git a/Sources/ParseSwift/Parse.swift b/Sources/ParseSwift/Parse.swift index dcacd3242..ee453a7e6 100644 --- a/Sources/ParseSwift/Parse.swift +++ b/Sources/ParseSwift/Parse.swift @@ -52,7 +52,7 @@ public struct ParseConfiguration { public internal(set) var cacheDiskCapacity = 10_000_000 /// If your app previously used the iOS Objective-C SDK, setting this value - /// to `true` will attempt to migrate relevant data stored in the Keychain to + /// to **true** will attempt to migrate relevant data stored in the Keychain to /// ParseSwift. Defaults to **false**. public internal(set) var isMigratingFromObjcSDK: Bool = false @@ -91,7 +91,7 @@ public struct ParseConfiguration { - parameter cacheMemoryCapacity: The memory capacity of the cache, in bytes. Defaults to 512KB. - parameter cacheDiskCapacity: The disk capacity of the cache, in bytes. Defaults to 10MB. - parameter migratingFromObjcSDK: If your app previously used the iOS Objective-C SDK, setting this value - to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. + to **true** will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. - parameter deletingKeychainIfNeeded: Deletes the Parse Keychain when the app is running for the first time. Defaults to **false**. - parameter httpAdditionalHeaders: A dictionary of additional headers to send with requests. See Apple's @@ -256,7 +256,7 @@ public struct ParseSwift { - parameter cacheMemoryCapacity: The memory capacity of the cache, in bytes. Defaults to 512KB. - parameter cacheDiskCapacity: The disk capacity of the cache, in bytes. Defaults to 10MB. - parameter migratingFromObjcSDK: If your app previously used the iOS Objective-C SDK, setting this value - to `true` will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. + to **true** will attempt to migrate relevant data stored in the Keychain to ParseSwift. Defaults to **false**. - parameter deletingKeychainIfNeeded: Deletes the Parse Keychain when the app is running for the first time. Defaults to **false**. - parameter httpAdditionalHeaders: A dictionary of additional headers to send with requests. See Apple's diff --git a/Sources/ParseSwift/Types/ParseACL.swift b/Sources/ParseSwift/Types/ParseACL.swift index 7d7902b9f..709b79b99 100644 --- a/Sources/ParseSwift/Types/ParseACL.swift +++ b/Sources/ParseSwift/Types/ParseACL.swift @@ -86,7 +86,7 @@ public struct ParseACL: ParseType, Returns true if a particular key has a specific access level. - parameter key: The key of the `ParseUser` or `ParseRole` for which to retrieve access. - parameter access: The type of access. - - returns: `true` if the `key` has *explicit* access, otherwise **false**. + - returns: **true** if the `key` has *explicit* access, otherwise **false**. */ func get(_ key: String, access: Access) -> Bool { guard let acl = acl else { // no acl, all open! @@ -98,11 +98,11 @@ public struct ParseACL: ParseType, // MARK: ParseUser /** Gets whether the given `objectId` is *explicitly* allowed to read this object. - Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns `true` + Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns **true** or if the user belongs to a role that has access. - parameter objectId: The `ParseUser.objectId` of the user for which to retrieve access. - - returns: `true` if the user with this `objectId` has *explicit* read access, otherwise **false**. + - returns: **true** if the user with this `objectId` has *explicit* read access, otherwise **false**. */ public func getReadAccess(objectId: String) -> Bool { get(objectId, access: .read) @@ -110,11 +110,11 @@ public struct ParseACL: ParseType, /** Gets whether the given `ParseUser` is *explicitly* allowed to read this object. - Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns `true` + Even if this returns **false**, the user may still be able to access it if `publicReadAccess` returns **true** or if the user belongs to a role that has access. - parameter user: The `ParseUser` for which to retrieve access. - - returns: `true` if the user with this `ParseUser` has *explicit* read access, otherwise **false**. + - returns: **true** if the user with this `ParseUser` has *explicit* read access, otherwise **false**. */ public func getReadAccess(user: T) -> Bool where T: ParseUser { if let objectId = user.objectId { @@ -126,11 +126,11 @@ public struct ParseACL: ParseType, /** Gets whether the given `objectId` is *explicitly* allowed to write this object. - Even if this returns false, the user may still be able to write it if `publicWriteAccess` returns `true` + Even if this returns false, the user may still be able to write it if `publicWriteAccess` returns **true** or if the user belongs to a role that has access. - parameter objectId: The `ParseUser.objectId` of the user for which to retrieve access. - - returns: `true` if the user with this `ParseUser.objectId` has *explicit* write access, otherwise **false**. + - returns: **true** if the user with this `ParseUser.objectId` has *explicit* write access, otherwise **false**. */ public func getWriteAccess(objectId: String) -> Bool { return get(objectId, access: .write) @@ -138,11 +138,11 @@ public struct ParseACL: ParseType, /** Gets whether the given `ParseUser` is *explicitly* allowed to write this object. - Even if this returns false, the user may still be able to write it if `publicWriteAccess` returns `true` + Even if this returns false, the user may still be able to write it if `publicWriteAccess` returns **true** or if the user belongs to a role that has access. - parameter user: The `ParseUser` of the user for which to retrieve access. - - returns: `true` if the `ParseUser` has *explicit* write access, otherwise **false**. + - returns: **true** if the `ParseUser` has *explicit* write access, otherwise **false**. */ public func getWriteAccess(user: T) -> Bool where T: ParseUser { if let objectId = user.objectId { @@ -203,7 +203,7 @@ public struct ParseACL: ParseType, Even if this returns **false**, the role may still be able to read it if a parent role has read access. - parameter roleName: The name of the role. - - returns: `true` if the role has read access, otherwise **false**. + - returns: **true** if the role has read access, otherwise **false**. */ public func getReadAccess(roleName: String) -> Bool { get(Self.getRoleAccessName(roleName), access: .read) @@ -214,7 +214,7 @@ public struct ParseACL: ParseType, Even if this returns **false**, the role may still be able to read it if a parent role has read access. - parameter role: The `ParseRole` to get access for. - - returns: `true` if the `ParseRole` has read access, otherwise **false**. + - returns: **true** if the `ParseRole` has read access, otherwise **false**. */ public func getReadAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } @@ -226,7 +226,7 @@ public struct ParseACL: ParseType, Even if this returns **false**, the role may still be able to write it if a parent role has write access. - parameter roleName: The name of the role. - - returns: `true` if the role has read access, otherwise **false**. + - returns: **true** if the role has read access, otherwise **false**. */ public func getWriteAccess(roleName: String) -> Bool { get(Self.getRoleAccessName(roleName), access: .write) @@ -237,7 +237,7 @@ public struct ParseACL: ParseType, Even if this returns **false**, the role may still be able to write it if a parent role has write access. - parameter role: The `ParseRole` to get access for. - - returns: `true` if the role has read access, otherwise **false**. + - returns: **true** if the role has read access, otherwise **false**. */ public func getWriteAccess(role: T) -> Bool where T: ParseRole { guard let name = role.name else { return false } @@ -372,7 +372,7 @@ extension ParseACL { This value will be copied and used as a template for the creation of new ACLs, so changes to the instance after this method has been called will not be reflected in new instance of `ParseObject`. - - parameter withAccessForCurrentUser: If `true`, the `ACL` that is applied to + - parameter withAccessForCurrentUser: If **true**, the `ACL` that is applied to newly-created instance of `ParseObject` will provide read and write access to the `ParseUser.+currentUser` at the time of creation. - If **false**, the provided `acl` will be used without modification. diff --git a/Sources/ParseSwift/Types/ParseConfig+async.swift b/Sources/ParseSwift/Types/ParseConfig+async.swift index e04de6182..a2db0e8df 100644 --- a/Sources/ParseSwift/Types/ParseConfig+async.swift +++ b/Sources/ParseSwift/Types/ParseConfig+async.swift @@ -33,7 +33,7 @@ public extension ParseConfig { /** Update the Config *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: `true` if saved, **false** if save is unsuccessful. + - returns: **true** if saved, **false** if save is unsuccessful. - throws: An error of type `ParseError`. */ func save(options: API.Options = []) async throws -> Bool { diff --git a/Sources/ParseSwift/Types/ParseConfig.swift b/Sources/ParseSwift/Types/ParseConfig.swift index 835d718b3..2cc48e4c4 100644 --- a/Sources/ParseSwift/Types/ParseConfig.swift +++ b/Sources/ParseSwift/Types/ParseConfig.swift @@ -72,7 +72,7 @@ extension ParseConfig { /** Update the Config *synchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns `true` if updated, **false** otherwise. + - returns: Returns **true** if updated, **false** otherwise. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index 1edcf3dfa..d885747ea 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -13,7 +13,7 @@ extension ParsePointer { /** Determines if two objects have the same objectId. - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: ParsePointer) -> Bool { return other.className == className && other.objectId == objectId @@ -81,7 +81,7 @@ public extension Pointer { /** Determines if a `ParseObject` and `Pointer`have the same `objectId`. - parameter as: `ParseObject` to compare. - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: T) -> Bool { return other.className == className && other.objectId == objectId @@ -90,7 +90,7 @@ public extension Pointer { /** Determines if two `Pointer`'s have the same `objectId`. - parameter as: `Pointer` to compare. - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ func hasSameObjectId(as other: Self) -> Bool { return other.className == className && other.objectId == objectId diff --git a/Sources/ParseSwift/Types/QueryConstraint.swift b/Sources/ParseSwift/Types/QueryConstraint.swift index 6edfb7066..09fa31ebb 100644 --- a/Sources/ParseSwift/Types/QueryConstraint.swift +++ b/Sources/ParseSwift/Types/QueryConstraint.swift @@ -511,7 +511,7 @@ public func near(key: String, geoPoint: ParseGeoPoint) -> QueryConstraint { - parameter key: The key to be constrained. - parameter geoPoint: The reference point as a `ParseGeoPoint`. - parameter distance: Maximum distance in radians. - - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. + - parameter sorted: **true** if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ @@ -537,7 +537,7 @@ public func withinRadians(key: String, - parameter key: The key to be constrained. - parameter geoPoint: The reference point represented as a `ParseGeoPoint`. - parameter distance: Maximum distance in miles. - - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. + - parameter sorted: **true** if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ @@ -558,7 +558,7 @@ public func withinMiles(key: String, - parameter key: The key to be constrained. - parameter geoPoint: The reference point represented as a `ParseGeoPoint`. - parameter distance: Maximum distance in kilometers. - - parameter sorted: `true` if results should be sorted by distance ascending, **false** is no sorting is required. + - parameter sorted: **true** if results should be sorted by distance ascending, **false** is no sorting is required. Defaults to true. - returns: The same instance of `QueryConstraint` as the receiver. */ diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index f02a1414e..dfffb7097 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -156,7 +156,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length - parameter lhs: first object to compare - parameter rhs: second object to compare - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ public static func == (lhs: ParseObjectTests.GameScoreClass, rhs: ParseObjectTests.GameScoreClass) -> Bool { @@ -213,7 +213,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length - parameter lhs: first object to compare - parameter rhs: second object to compare - - returns: Returns a `true` if the other object has the same `objectId` or **false** if unsuccessful. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. */ public static func == (lhs: ParseObjectTests.GameClass, rhs: ParseObjectTests.GameClass) -> Bool { lhs.hasSameObjectId(as: rhs) From eda6213637d5347d851fff86fbc6593b54b2769c Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 13:49:04 -0400 Subject: [PATCH 12/55] add async/await methods --- ParseSwift.xcodeproj/project.pbxproj | 20 - .../Objects/ParseInstallation+async.swift | 3 +- .../Objects/ParseInstallation+combine.swift | 4 +- .../Objects/ParseInstallation.swift | 6 +- .../Objects/ParseObject+async.swift | 2 +- .../Objects/ParseObject+combine.swift | 2 +- Sources/ParseSwift/Objects/ParseObject.swift | 4 +- .../ParseSwift/Objects/ParseUser+async.swift | 2 +- .../Objects/ParseUser+combine.swift | 2 +- Sources/ParseSwift/Objects/ParseUser.swift | 4 +- .../Protocols/ParseSchemable+async.swift | 30 - .../ParseSwift/Protocols/ParseSchemable.swift | 574 ------------------ Sources/ParseSwift/Types/ParseCLP.swift | 12 + .../ParseSwift/Types/ParseSchema+async.swift | 72 ++- Sources/ParseSwift/Types/Pointer+async.swift | 2 +- .../ParseSwift/Types/Pointer+combine.swift | 2 +- Sources/ParseSwift/Types/Pointer.swift | 2 +- 17 files changed, 98 insertions(+), 645 deletions(-) delete mode 100644 Sources/ParseSwift/Protocols/ParseSchemable+async.swift delete mode 100644 Sources/ParseSwift/Protocols/ParseSchemable.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 5d0e31dd3..0408af303 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -362,14 +362,6 @@ 709A14922839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; 709A14932839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; 709A14942839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; - 709A14962839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; - 709A14972839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; - 709A14982839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; - 709A14992839B72300BF85E5 /* ParseSchemable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14952839B72300BF85E5 /* ParseSchemable.swift */; }; - 709A149B2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; - 709A149C2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; - 709A149D2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; - 709A149E2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */; }; 709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A12839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A22839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; @@ -1005,8 +997,6 @@ 709A148628396B1C00BF85E5 /* ParseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseField.swift; sourceTree = ""; }; 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; - 709A14952839B72300BF85E5 /* ParseSchemable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemable.swift; sourceTree = ""; }; - 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchemable+async.swift"; sourceTree = ""; }; 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1454,8 +1444,6 @@ 709A14902839A60600BF85E5 /* ParseIndex.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, - 709A14952839B72300BF85E5 /* ParseSchemable.swift */, - 709A149A2839BA3900BF85E5 /* ParseSchemable+async.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, 91BB8FCE2690BA70005A6BA5 /* QueryObservable.swift */, F97B45C724D9C6F200F4A88B /* Savable.swift */, @@ -2248,7 +2236,6 @@ 91F346B9269B766C005727B6 /* CloudViewModel.swift in Sources */, 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */, - 709A149B2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, 7028373426BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503825B406B800B5DBC2 /* ParseSession.swift in Sources */, 7044C1C825C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, @@ -2355,7 +2342,6 @@ 7045769D26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B463324D9C74400F4A88B /* URLSession.swift in Sources */, F97B464E24D9C78B00F4A88B /* Add.swift in Sources */, - 709A14962839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095326BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9890252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FE24D9C6F200F4A88B /* ParseFile.swift in Sources */, @@ -2490,7 +2476,6 @@ 91F346BA269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */, - 709A149C2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, 7028373526BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503925B406B800B5DBC2 /* ParseSession.swift in Sources */, 7044C1C925C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, @@ -2597,7 +2582,6 @@ 7045769E26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B463424D9C74400F4A88B /* URLSession.swift in Sources */, F97B464F24D9C78B00F4A88B /* Add.swift in Sources */, - 709A14972839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095426BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9891252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FF24D9C6F200F4A88B /* ParseFile.swift in Sources */, @@ -2835,7 +2819,6 @@ 91F346BC269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E924D9C6F200F4A88B /* Query.swift in Sources */, - 709A149E2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, F97B463624D9C74400F4A88B /* URLSession.swift in Sources */, 7028373726BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503B25B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2942,7 +2925,6 @@ 704576A026BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B464924D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */, - 709A14992839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095626BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9893252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465524D9C78C00F4A88B /* AddUnique.swift in Sources */, @@ -2983,7 +2965,6 @@ 91F346BB269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */, F97B45E824D9C6F200F4A88B /* Query.swift in Sources */, - 709A149D2839BA3900BF85E5 /* ParseSchemable+async.swift in Sources */, F97B463524D9C74400F4A88B /* URLSession.swift in Sources */, 7028373626BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503A25B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -3090,7 +3071,6 @@ 7045769F26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B464824D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */, - 709A14982839B72300BF85E5 /* ParseSchemable.swift in Sources */, 703B095526BF47FD005A112F /* ParseTwitter+async.swift in Sources */, 70BC9892252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465424D9C78C00F4A88B /* AddUnique.swift in Sources */, diff --git a/Sources/ParseSwift/Objects/ParseInstallation+async.swift b/Sources/ParseSwift/Objects/ParseInstallation+async.swift index 0cb176c15..095857cea 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+async.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+async.swift @@ -13,8 +13,7 @@ public extension ParseInstallation { // MARK: Async/Await /** - Fetches the `ParseInstallation` *aynchronously* with the current data from the server - and sets an error if one occurs. + Fetches the `ParseInstallation` *aynchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. diff --git a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift index 4265326df..07020cd04 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift @@ -14,8 +14,8 @@ public extension ParseInstallation { // MARK: Combine /** - Fetches the `ParseInstallation` *aynchronously* with the current data from the server - and sets an error if one occurs. Publishes when complete. + Fetches the `ParseInstallation` *aynchronously* with the current data from the server. + Publishes when complete. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. diff --git a/Sources/ParseSwift/Objects/ParseInstallation.swift b/Sources/ParseSwift/Objects/ParseInstallation.swift index bdd7979c4..9b3853f35 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation.swift @@ -424,8 +424,7 @@ extension ParseInstallation { } /** - Fetches the `ParseInstallation` *synchronously* with the current data from the server - and sets an error if one occurs. + Fetches the `ParseInstallation` *synchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. @@ -814,8 +813,7 @@ extension ParseInstallation { // MARK: Deletable extension ParseInstallation { /** - Deletes the `ParseInstallation` *synchronously* with the current data from the server - and sets an error if one occurs. + Deletes the `ParseInstallation` *synchronously* with the current data from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. diff --git a/Sources/ParseSwift/Objects/ParseObject+async.swift b/Sources/ParseSwift/Objects/ParseObject+async.swift index aacc46fe3..39f8af389 100644 --- a/Sources/ParseSwift/Objects/ParseObject+async.swift +++ b/Sources/ParseSwift/Objects/ParseObject+async.swift @@ -13,7 +13,7 @@ public extension ParseObject { // MARK: Async/Await /** - Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *aynchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. diff --git a/Sources/ParseSwift/Objects/ParseObject+combine.swift b/Sources/ParseSwift/Objects/ParseObject+combine.swift index cc79fdbc3..c320750e1 100644 --- a/Sources/ParseSwift/Objects/ParseObject+combine.swift +++ b/Sources/ParseSwift/Objects/ParseObject+combine.swift @@ -14,7 +14,7 @@ public extension ParseObject { // MARK: Combine /** - Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *aynchronously* with the current data from the server. Publishes when complete. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 018a41999..ce4a4d039 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -817,7 +817,7 @@ extension ParseObject { extension ParseObject { /** - Fetches the `ParseObject` *synchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *synchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. @@ -1211,7 +1211,7 @@ internal extension ParseType { // MARK: Deletable extension ParseObject { /** - Deletes the `ParseObject` *synchronously* with the current data from the server and sets an error if one occurs. + Deletes the `ParseObject` *synchronously* with the current data from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. diff --git a/Sources/ParseSwift/Objects/ParseUser+async.swift b/Sources/ParseSwift/Objects/ParseUser+async.swift index 725865460..29e8baf18 100644 --- a/Sources/ParseSwift/Objects/ParseUser+async.swift +++ b/Sources/ParseSwift/Objects/ParseUser+async.swift @@ -182,7 +182,7 @@ public extension ParseUser { } /** - Fetches the `ParseUser` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseUser` *aynchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. diff --git a/Sources/ParseSwift/Objects/ParseUser+combine.swift b/Sources/ParseSwift/Objects/ParseUser+combine.swift index b3245da93..bd4be53ff 100644 --- a/Sources/ParseSwift/Objects/ParseUser+combine.swift +++ b/Sources/ParseSwift/Objects/ParseUser+combine.swift @@ -171,7 +171,7 @@ public extension ParseUser { } /** - Fetches the `ParseUser` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseUser` *aynchronously* with the current data from the server. Publishes when complete. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index 3ba412338..89da293ed 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -833,7 +833,7 @@ extension ParseUser { } /** - Fetches the `ParseUser` *synchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseUser` *synchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and `includeAll` for `Query`. @@ -1245,7 +1245,7 @@ extension ParseUser { // MARK: Deletable extension ParseUser { /** - Deletes the `ParseUser` *synchronously* with the current data from the server and sets an error if one occurs. + Deletes the `ParseUser` *synchronously* with the current data from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. diff --git a/Sources/ParseSwift/Protocols/ParseSchemable+async.swift b/Sources/ParseSwift/Protocols/ParseSchemable+async.swift deleted file mode 100644 index 85526a998..000000000 --- a/Sources/ParseSwift/Protocols/ParseSchemable+async.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ParseSchemable+async.swift -// ParseSwift -// -// Created by Corey Baker on 5/21/22. -// Copyright © 2022 Parse Community. All rights reserved. -// - -#if compiler(>=5.5.2) && canImport(_Concurrency) -import Foundation - -extension ParseSchemable { - /** - Fetches the `ParseSchema` *aynchronously* with the current data from the server and sets an error if one occurs. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns the fetched `ParseSchema`. - - throws: An error of type `ParseError`. - - important: If an object fetched has the same objectId as current, it will automatically update the current. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - func fetch(options: API.Options = []) async throws -> Self { - try await withCheckedThrowingContinuation { continuation in - self.fetch(options: options, - completion: continuation.resume) - } - } -} - -#endif diff --git a/Sources/ParseSwift/Protocols/ParseSchemable.swift b/Sources/ParseSwift/Protocols/ParseSchemable.swift deleted file mode 100644 index c7dd74924..000000000 --- a/Sources/ParseSwift/Protocols/ParseSchemable.swift +++ /dev/null @@ -1,574 +0,0 @@ -// -// ParseSchemable.swift -// ParseSwift -// -// Created by Corey Baker on 5/21/22. -// Copyright © 2022 Parse Community. All rights reserved. -// - -import Foundation - -protocol ParseSchemable: ParseType, Decodable { - - /// The class name of the Schema. - var className: String { get set } - - /// The session token for this session. - var fields: [String: ParseField]? { get set } - - /// The session token for this session. - var indexes: [String: AnyCodable]? { get set } -} - -// MARK: Default Implementations -extension ParseSchemable { - - /** - Add a Field to create/update a `ParseSchema`. - - - parameter name: Name of the index that will be created/updated on Parse Server. - - parameter index: The `ParseIndex` to add that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - */ - public func addIndex(_ name: String, - index: ParseIndex) -> Self { - var mutableSchema = self - - if mutableSchema.indexes != nil { - mutableSchema.indexes?[name] = AnyCodable(index) - } else { - mutableSchema.indexes = [name: AnyCodable(index)] - } - - return mutableSchema - } - - /** - Add a Field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - throws: An error of type `ParseError`. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addField(_ name: String, - type: ParseFieldType, - target: T, - options: ParseFieldOptions) throws -> Self where T: ParseObject { - switch type { - case .pointer: - return try addPointer(name, target: target, options: options) - case .relation: - return try addRelation(name, target: target) - default: - return addField(name, type: type, options: options) - } - } - - /** - Add a Field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addField(_ name: String, - type: ParseFieldType, - options: ParseFieldOptions) -> Self { - var mutableSchema = self - let field = ParseField(type: type, options: options) - if mutableSchema.fields != nil { - mutableSchema.fields?[name] = field - } else { - mutableSchema.fields = [name: field] - } - - return mutableSchema - } - - /** - Add a String field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addString(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .string, options: options) - } - - /** - Add a Number field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addNumber(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .number, options: options) - } - - /** - Add a Boolean field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addBoolean(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .boolean, options: options) - } - - /** - Add a Date field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addDate(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .date, options: options) - } - - /** - Add a File field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addFile(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .file, options: options) - } - - /** - Add a GeoPoint field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addGeoPoint(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .geoPoint, options: options) - } - - /** - Add a Polygon field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addPolygon(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .polygon, options: options) - } - - /** - Add an Object field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addObject(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .object, options: options) - } - - /** - Add a Bytes field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addBytes(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .bytes, options: options) - } - - /** - Add an Array field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addArray(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .array, options: options) - } - - /** - Add a Pointer field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. - Defaults to **nil**. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - throws: An error of type `ParseError`. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addPointer(_ name: String, - target: T?, - options: ParseFieldOptions) throws -> Self where T: ParseObject { - guard let target = target else { - throw ParseError(code: .unknownError, message: "Target must not be nil") - } - - let field = ParseField(type: .pointer, target: target, options: options) - var mutableSchema = self - if mutableSchema.fields != nil { - mutableSchema.fields?[name] = field - } else { - mutableSchema.fields = [name: field] - } - - return mutableSchema - } - - /** - Add a Relation field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. - Defaults to **nil**. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - throws: An error of type `ParseError`. - */ - func addRelation(_ name: String, - target: T?) throws -> Self where T: ParseObject { - guard let target = target else { - throw ParseError(code: .unknownError, message: "Target must not be nil") - } - - let field = ParseField(type: .relation, target: target) - var mutableSchema = self - if mutableSchema.fields != nil { - mutableSchema.fields?[name] = field - } else { - mutableSchema.fields = [name: field] - } - - return mutableSchema - } - - /** - Delete a field in the `ParseSchema`. - - - parameter name: Name of the field that will be deleted on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - */ - func deleteField(_ name: String) -> Self { - let field = ParseField(operation: .delete) - var mutableSchema = self - if mutableSchema.fields != nil { - mutableSchema.fields?[name] = field - } else { - mutableSchema.fields = [name: field] - } - - return mutableSchema - } - - /** - Delete an index in the `ParseSchema`. - - - parameter name: Name of the index that will be deleted on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - */ - func deleteIndex(_ name: String) -> Self { - let index = AnyCodable(Delete()) - var mutableSchema = self - if mutableSchema.indexes != nil { - mutableSchema.indexes?[name] = index - } else { - mutableSchema.indexes = [name: index] - } - - return mutableSchema - } -} - -// MARK: Convenience -extension ParseSchemable { - - var endpoint: API.Endpoint { - .schema(className: className) - } - - var endpointPurge: API.Endpoint { - .purge(className: className) - } -} - -// MARK: Fetchable -extension ParseSchemable { - - /** - Fetches the `ParseSchema` *asynchronously* and executes the given callback block. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - public func fetch(options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try fetchCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } - } - - func fetchCommand() throws -> API.Command { - - return API.Command(method: .GET, - path: endpoint) { (data) -> Self in - try ParseCoding.jsonDecoder().decode(Self.self, from: data) - } - } -} - -// MARK: Savable -extension ParseSchemable { - - /** - Creates the `ParseSchema` *asynchronously* and executes the given callback block. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - public func create(options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try createCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } - } - - /** - Updates the `ParseSchema` *asynchronously* and executes the given callback block. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - public func update(options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try updateCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } - } - - func createCommand() throws -> API.Command { - - return API.Command(method: .POST, - path: endpoint, - body: self) { (data) -> Self in - try ParseCoding.jsonDecoder().decode(Self.self, from: data) - } - } - - func updateCommand() throws -> API.Command { - - API.Command(method: .PUT, - path: endpoint, - body: self) { (data) -> Self in - try ParseCoding.jsonDecoder().decode(Self.self, from: data) - } - } -} - -// MARK: Deletable -extension ParseSchemable { - - /** - Deletes all objects in the `ParseSchema` *asynchronously* and executes the given callback block. - - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - public func purge( - options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void - ) { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try deleteCommand().executeAsync(options: options, - callbackQueue: callbackQueue) { result in - switch result { - - case .success: - completion(.success(())) - case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } - } - } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } - } catch { - callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) - } - } - } - - /** - Deletes the `ParseSchema` *asynchronously* and executes the given callback block. - - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` - currently contains objects, run `purge()` first. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - public func delete( - options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void - ) { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try deleteCommand().executeAsync(options: options, - callbackQueue: callbackQueue) { result in - switch result { - - case .success: - completion(.success(())) - case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } - } - } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } - } catch { - callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) - } - } - } - - func purgeCommand() throws -> API.Command { - - API.Command(method: .DELETE, - path: endpointPurge) { (data) -> NoBody in - let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) - if let error = error { - throw error - } else { - return NoBody() - } - } - } - - func deleteCommand() throws -> API.Command { - - API.Command(method: .DELETE, - path: endpoint, - body: self) { (data) -> NoBody in - let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) - if let error = error { - throw error - } else { - return NoBody() - } - } - } -} diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index c813686a5..7afbe7cd2 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -119,6 +119,7 @@ public extension ParseCLP { - parameter user: The `ParseUser` access to check. - returns: The protected fields. + - throws: An error of type `ParseError`. */ func getProtected(_ user: U) throws -> [String] where U: ParseUser { let objectId = try user.toPointer().objectId @@ -140,6 +141,7 @@ public extension ParseCLP { - parameter role: The `ParseRole` access to check. - returns: The protected fields. + - throws: An error of type `ParseError`. */ func getProtected(_ role: R) throws -> [String] where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) @@ -163,6 +165,7 @@ public extension ParseCLP { - parameter for: The `ParseUser` to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setProtected(_ fields: [String], for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId @@ -186,6 +189,7 @@ public extension ParseCLP { - parameter for: The `ParseRole` to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setProtected(_ fields: [String], for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) @@ -595,6 +599,7 @@ public extension ParseCLP { - returns: **true** if authentication is required, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. + - throws: An error of type `ParseError`. */ func doesHaveWriteAccess(_ user: U, checkAddField: Bool = false) throws -> Bool where U: ParseUser { @@ -610,6 +615,7 @@ public extension ParseCLP { - returns: **true** if authentication is required, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. + - throws: An error of type `ParseError`. */ func doesHaveWriteAccess(_ role: R, checkAddField: Bool = false) throws -> Bool where R: ParseRole { @@ -666,6 +672,7 @@ public extension ParseCLP { - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setWriteAccess(_ user: U, to allow: Bool, @@ -697,6 +704,7 @@ public extension ParseCLP { - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setWriteAccess(_ role: R, to allow: Bool, @@ -765,6 +773,7 @@ public extension ParseCLP { Check whether the `ParseUser` has access to read from this class. - parameter user: The `ParseUser` to check. - returns: **true** if authentication is required, **false** otherwise. + - throws: An error of type `ParseError`. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ @@ -777,6 +786,7 @@ public extension ParseCLP { Check whether the `ParseRole` has access to read from this class. - parameter role: The `ParseRole` to check. - returns: **true** if authentication is required, **false** otherwise. + - throws: An error of type `ParseError`. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ @@ -825,6 +835,7 @@ public extension ParseCLP { - parameter role: The `ParseUser` to provide/restrict access to. - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setReadAccess(_ user: U, to allow: Bool) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId @@ -849,6 +860,7 @@ public extension ParseCLP { - parameter role: The `ParseRole` to provide/restrict access to. - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ func setReadAccess(_ role: R, to allow: Bool) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) diff --git a/Sources/ParseSwift/Types/ParseSchema+async.swift b/Sources/ParseSwift/Types/ParseSchema+async.swift index ce9008600..a0afc52ac 100644 --- a/Sources/ParseSwift/Types/ParseSchema+async.swift +++ b/Sources/ParseSwift/Types/ParseSchema+async.swift @@ -11,11 +11,10 @@ import Foundation public extension ParseSchema { /** - Fetches the `ParseSchema` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseSchema` *aynchronously* with the current data from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. - - important: If an object fetched has the same objectId as current, it will automatically update the current. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -25,6 +24,75 @@ public extension ParseSchema { completion: continuation.resume) } } + + /** + Creates the `ParseSchema` *aynchronously* with the current data from the server. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func create(options: API.Options = []) async throws -> Self { + try await withCheckedThrowingContinuation { continuation in + self.create(options: options, + completion: continuation.resume) + } + } + + /** + Updates the `ParseSchema` *aynchronously* with the current data from the server. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func update(options: API.Options = []) async throws -> Self { + try await withCheckedThrowingContinuation { continuation in + self.update(options: options, + completion: continuation.resume) + } + } + + /** + Purges the `ParseSchema` *aynchronously* with the current data from the server. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func purge(options: API.Options = []) async throws { + let result = try await withCheckedThrowingContinuation { continuation in + self.purge(options: options, + completion: continuation.resume) + } + if case let .failure(error) = result { + throw error + } + } + + /** + Deletes the `ParseSchema` *aynchronously* with the current data from the server. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the fetched `ParseSchema`. + - throws: An error of type `ParseError`. + - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` + currently contains objects, run `purge()` first. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func delete(options: API.Options = []) async throws { + let result = try await withCheckedThrowingContinuation { continuation in + self.delete(options: options, + completion: continuation.resume) + } + if case let .failure(error) = result { + throw error + } + } } #endif diff --git a/Sources/ParseSwift/Types/Pointer+async.swift b/Sources/ParseSwift/Types/Pointer+async.swift index 3bb86e5e6..7a2ddfcff 100644 --- a/Sources/ParseSwift/Types/Pointer+async.swift +++ b/Sources/ParseSwift/Types/Pointer+async.swift @@ -12,7 +12,7 @@ import Foundation // MARK: Async/Await public extension Pointer { /** - Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *aynchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and `includeAll` for `Query`. diff --git a/Sources/ParseSwift/Types/Pointer+combine.swift b/Sources/ParseSwift/Types/Pointer+combine.swift index b4e5380b5..b28311a7d 100644 --- a/Sources/ParseSwift/Types/Pointer+combine.swift +++ b/Sources/ParseSwift/Types/Pointer+combine.swift @@ -13,7 +13,7 @@ import Combine // MARK: Combine public extension Pointer { /** - Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *aynchronously* with the current data from the server. Publishes when complete. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index d885747ea..f61cc6def 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -97,7 +97,7 @@ public extension Pointer { } /** - Fetches the `ParseObject` *synchronously* with the current data from the server and sets an error if one occurs. + Fetches the `ParseObject` *synchronously* with the current data from the server. - parameter includeKeys: The name(s) of the key(s) to include that are `ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and `includeAll` for `Query`. From 17a4aa6b93a9d43f53845b515c2ad8c9101c88c1 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 14:07:11 -0400 Subject: [PATCH 13/55] add Combine methods --- ParseSwift.xcodeproj/project.pbxproj | 10 ++ .../Types/ParseConfig+combine.swift | 2 +- .../Types/ParseHealth+combine.swift | 2 +- .../Types/ParseOperation+combine.swift | 2 +- .../ParseSwift/Types/ParseSchema+async.swift | 10 +- .../Types/ParseSchema+combine.swift | 93 +++++++++++++++++++ Sources/ParseSwift/Types/ParseSchema.swift | 10 +- 7 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 Sources/ParseSwift/Types/ParseSchema+combine.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 0408af303..9e255e1be 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -366,6 +366,10 @@ 709A14A12839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A22839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A32839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; + 709A14A5283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; + 709A14A6283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; + 709A14A7283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; + 709A14A8283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -998,6 +1002,7 @@ 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; + 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1722,6 +1727,7 @@ 7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */, 709A147C283949D100BF85E5 /* ParseSchema.swift */, 709A148128395ED100BF85E5 /* ParseSchema+async.swift */, + 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */, 91679D63268E596300F71809 /* ParseVersion.swift */, F97B45BE24D9C6F200F4A88B /* Pointer.swift */, 70C167B327304F09009F4E30 /* Pointer+async.swift */, @@ -2235,6 +2241,7 @@ 916786E2259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346B9269B766C005727B6 /* CloudViewModel.swift in Sources */, 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */, + 709A14A5283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */, F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */, 7028373426BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503825B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2475,6 +2482,7 @@ 916786E3259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BA269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */, + 709A14A6283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */, F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */, 7028373526BD8883007688C9 /* ParseObject+async.swift in Sources */, 70C5503925B406B800B5DBC2 /* ParseSession.swift in Sources */, @@ -2818,6 +2826,7 @@ 916786E5259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BC269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */, + 709A14A8283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */, F97B45E924D9C6F200F4A88B /* Query.swift in Sources */, F97B463624D9C74400F4A88B /* URLSession.swift in Sources */, 7028373726BD8883007688C9 /* ParseObject+async.swift in Sources */, @@ -2964,6 +2973,7 @@ 916786E4259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, 91F346BB269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */, + 709A14A7283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */, F97B45E824D9C6F200F4A88B /* Query.swift in Sources */, F97B463524D9C74400F4A88B /* URLSession.swift in Sources */, 7028373626BD8883007688C9 /* ParseObject+async.swift in Sources */, diff --git a/Sources/ParseSwift/Types/ParseConfig+combine.swift b/Sources/ParseSwift/Types/ParseConfig+combine.swift index 347b03f6b..50e85a793 100644 --- a/Sources/ParseSwift/Types/ParseConfig+combine.swift +++ b/Sources/ParseSwift/Types/ParseConfig+combine.swift @@ -29,7 +29,7 @@ public extension ParseConfig { } /** - Update the Config *asynchronously*. + Update the Config *asynchronously*. Publishes when complete. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. */ diff --git a/Sources/ParseSwift/Types/ParseHealth+combine.swift b/Sources/ParseSwift/Types/ParseHealth+combine.swift index 660ff4603..e4873fbab 100644 --- a/Sources/ParseSwift/Types/ParseHealth+combine.swift +++ b/Sources/ParseSwift/Types/ParseHealth+combine.swift @@ -15,7 +15,7 @@ public extension ParseHealth { // MARK: Combine /** - Calls the health check function *asynchronously*. + Calls the health check function *asynchronously*. Publishes when complete. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. */ diff --git a/Sources/ParseSwift/Types/ParseOperation+combine.swift b/Sources/ParseSwift/Types/ParseOperation+combine.swift index 988b68a57..c9274faeb 100644 --- a/Sources/ParseSwift/Types/ParseOperation+combine.swift +++ b/Sources/ParseSwift/Types/ParseOperation+combine.swift @@ -15,7 +15,7 @@ public extension ParseOperation { // MARK: Combine /** - Saves the operations on the `ParseObject` *asynchronously* and executes the given callback block. + Saves the operations on the `ParseObject` *asynchronously*. Publishes when complete. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. diff --git a/Sources/ParseSwift/Types/ParseSchema+async.swift b/Sources/ParseSwift/Types/ParseSchema+async.swift index a0afc52ac..848ca97e1 100644 --- a/Sources/ParseSwift/Types/ParseSchema+async.swift +++ b/Sources/ParseSwift/Types/ParseSchema+async.swift @@ -11,7 +11,7 @@ import Foundation public extension ParseSchema { /** - Fetches the `ParseSchema` *aynchronously* with the current data from the server. + Fetches the `ParseSchema` *aynchronously* from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. @@ -26,7 +26,7 @@ public extension ParseSchema { } /** - Creates the `ParseSchema` *aynchronously* with the current data from the server. + Creates the `ParseSchema` *aynchronously* on the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. @@ -41,7 +41,7 @@ public extension ParseSchema { } /** - Updates the `ParseSchema` *aynchronously* with the current data from the server. + Updates the `ParseSchema` *aynchronously* on the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. @@ -56,7 +56,7 @@ public extension ParseSchema { } /** - Purges the `ParseSchema` *aynchronously* with the current data from the server. + Deletes all objects in the `ParseSchema` *aynchronously* from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. @@ -75,7 +75,7 @@ public extension ParseSchema { } /** - Deletes the `ParseSchema` *aynchronously* with the current data from the server. + Deletes the `ParseSchema` *aynchronously* from the server. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns the fetched `ParseSchema`. - throws: An error of type `ParseError`. diff --git a/Sources/ParseSwift/Types/ParseSchema+combine.swift b/Sources/ParseSwift/Types/ParseSchema+combine.swift new file mode 100644 index 000000000..e8dae4827 --- /dev/null +++ b/Sources/ParseSwift/Types/ParseSchema+combine.swift @@ -0,0 +1,93 @@ +// +// ParseSchema+combine.swift +// ParseSwift +// +// Created by Corey Baker on 5/22/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +#if canImport(Combine) +import Foundation +import Combine + +public extension ParseSchema { + /** + Fetches the `ParseObject` *aynchronously* from the server. Publishes when complete. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func fetchPublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future { + Future { promise in + self.fetch(options: options, + completion: promise) + } + } + + /** + Creates the `ParseObject` *aynchronously* on the server. Publishes when complete. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func createPublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future { + Future { promise in + self.create(options: options, + completion: promise) + } + } + + /** + Updates the `ParseObject` *aynchronously* on the server. Publishes when complete. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func updatePublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future { + Future { promise in + self.update(options: options, + completion: promise) + } + } + + /** + Deletes all objects in the `ParseObject` *aynchronously* from the server. Publishes when complete. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. + */ + func purgePublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future { + Future { promise in + self.purge(options: options, + completion: promise) + } + } + + /** + Deletes the `ParseObject` *aynchronously* from the server. Publishes when complete. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces a single value and then finishes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` + currently contains objects, run `purge()` first. + */ + func deletePublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future { + Future { promise in + self.delete(options: options, + completion: promise) + } + } +} + +#endif diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index db7da6bf0..c29f3af37 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -379,7 +379,7 @@ extension ParseSchema { extension ParseSchema { /** - Fetches the `ParseSchema` *asynchronously* and executes the given callback block. + Fetches the `ParseSchema` *asynchronously* from the server and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. @@ -423,7 +423,7 @@ extension ParseSchema { extension ParseSchema { /** - Creates the `ParseSchema` *asynchronously* and executes the given callback block. + Creates the `ParseSchema` *asynchronously* on the server and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. @@ -455,7 +455,7 @@ extension ParseSchema { } /** - Updates the `ParseSchema` *asynchronously* and executes the given callback block. + Updates the `ParseSchema` *asynchronously* on the server and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. @@ -509,7 +509,7 @@ extension ParseSchema { extension ParseSchema { /** - Deletes all objects in the `ParseSchema` *asynchronously* and executes the given callback block. + Deletes all objects in the `ParseSchema` *asynchronously* from the server and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default @@ -552,7 +552,7 @@ extension ParseSchema { } /** - Deletes the `ParseSchema` *asynchronously* and executes the given callback block. + Deletes the `ParseSchema` *asynchronously* from the server and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default From 9c6750e57bc340c9b4ae9079213464b578ca5476 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 22 May 2022 22:37:01 -0400 Subject: [PATCH 14/55] fixes --- .../Contents.swift | 25 +++++++++++--- Sources/ParseSwift/API/API.swift | 2 +- Sources/ParseSwift/Types/ParseCLP.swift | 18 ++++++++++ Sources/ParseSwift/Types/ParseField.swift | 33 +++++++++++++++---- .../ParseSwift/Types/ParseSchema+async.swift | 5 +++ .../Types/ParseSchema+combine.swift | 5 +++ Sources/ParseSwift/Types/ParseSchema.swift | 28 ++++++++++++++++ 7 files changed, 105 insertions(+), 11 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index bba4c1f17..b2ee941c8 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -8,7 +8,7 @@ PlaygroundPage.current.needsIndefiniteExecution = true initializeParse() //: Create your own value typed `ParseObject`. -struct GameScore: ParseObject { +struct GameScore2: ParseObject { //: These are required by ParseObject var objectId: String? var createdAt: Date? @@ -32,7 +32,7 @@ struct GameScore: ParseObject { //: It's recommended to place custom initializers in an extension //: to preserve the memberwise initializer. -extension GameScore { +extension GameScore2 { init(points: Int) { self.points = points @@ -43,8 +43,25 @@ extension GameScore { } } -let schema = ParseSchema(classLevelPermissions: .init()) +//: First lets create a new CLP for the new schema. +let clp = ParseCLP(requireAuthentication: false, publicAccess: true) -print(schema.className) +//: Next we use the CLP to create the new schema and add fields to it. +var gameScoreSchema = ParseSchema(classLevelPermissions: clp) + .addField("points", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: 0)) + +print(gameScoreSchema) + +//: Now lets create the schema on the server. +gameScoreSchema.create { result in + switch result { + case .success(let savedSchema): + print("Check GameScore2 in Dashboard. \(savedSchema)") + case .failure(let error): + print("Couldn't save schema: \(error)") + } +} //: [Next](@next) diff --git a/Sources/ParseSwift/API/API.swift b/Sources/ParseSwift/API/API.swift index 477f1af35..98f834cdf 100644 --- a/Sources/ParseSwift/API/API.swift +++ b/Sources/ParseSwift/API/API.swift @@ -102,7 +102,7 @@ public struct API { case .schemas: return "/schemas" case .schema(let className): - return "/sessions/\(className)" + return "/schemas/\(className)" case .purge(let className): return "/purge/\(className)" case .triggers: diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 7afbe7cd2..9564a0a21 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -867,3 +867,21 @@ public extension ParseCLP { return setReadAccess(roleNameAccess, to: allow) } } + +// MARK: CustomDebugStringConvertible +extension ParseCLP: CustomDebugStringConvertible { + public var debugDescription: String { + guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), + let descriptionString = String(data: descriptionData, encoding: .utf8) else { + return "ParseCLP ()" + } + return "ParseCLP (\(descriptionString))" + } +} + +// MARK: CustomStringConvertible +extension ParseCLP: CustomStringConvertible { + public var description: String { + debugDescription + } +} diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index 19736ad10..ce996b89d 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -15,6 +15,10 @@ struct ParseField: Codable { var defaultValue: AnyCodable? var targetClass: String? + enum CodingKeys: String, CodingKey { + case type, required + } + init(operation: Operation) { __op = operation } @@ -22,13 +26,17 @@ struct ParseField: Codable { init(type: ParseFieldType, options: ParseFieldOptions) where V: Codable { self.type = type self.required = options.required - self.defaultValue = AnyCodable(options.defaultValue) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(defaultValue) + } } init(type: ParseFieldType, options: ParseFieldOptions) throws where V: ParseObject { self.type = type self.required = options.required - self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(try defaultValue.toPointer()) + } } init(type: ParseFieldType, @@ -49,7 +57,9 @@ struct ParseField: Codable { self.type = type self.targetClass = target?.className self.required = options.required - self.defaultValue = AnyCodable(options.defaultValue) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(defaultValue) + } } init(type: ParseFieldType, @@ -58,7 +68,9 @@ struct ParseField: Codable { self.type = type self.targetClass = target?.className self.required = options.required - self.defaultValue = AnyCodable(options.defaultValue) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(defaultValue) + } } init(type: ParseFieldType, @@ -67,7 +79,9 @@ struct ParseField: Codable { self.type = type self.targetClass = target?.className self.required = options.required - self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(try defaultValue.toPointer()) + } } init(type: ParseFieldType, @@ -76,7 +90,9 @@ struct ParseField: Codable { self.type = type self.targetClass = target?.className self.required = options.required - self.defaultValue = AnyCodable(try options.defaultValue?.toPointer()) + if let defaultValue = options.defaultValue { + self.defaultValue = AnyCodable(try defaultValue.toPointer()) + } } } @@ -106,6 +122,11 @@ public struct ParseFieldOptions: Codable { /// The default value for a field. var defaultValue: V? + + public init(required: Bool = false, defauleValue: V? = nil) { + self.required = required + self.defaultValue = defauleValue + } } /// Field types available in `ParseSchema`. diff --git a/Sources/ParseSwift/Types/ParseSchema+async.swift b/Sources/ParseSwift/Types/ParseSchema+async.swift index 848ca97e1..fc9e05b4f 100644 --- a/Sources/ParseSwift/Types/ParseSchema+async.swift +++ b/Sources/ParseSwift/Types/ParseSchema+async.swift @@ -17,6 +17,7 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func fetch(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -32,6 +33,7 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func create(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -47,6 +49,7 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func update(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -63,6 +66,7 @@ public extension ParseSchema { - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func purge(options: API.Options = []) async throws { let result = try await withCheckedThrowingContinuation { continuation in @@ -83,6 +87,7 @@ public extension ParseSchema { currently contains objects, run `purge()` first. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func delete(options: API.Options = []) async throws { let result = try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseSchema+combine.swift b/Sources/ParseSwift/Types/ParseSchema+combine.swift index e8dae4827..7fc90fc39 100644 --- a/Sources/ParseSwift/Types/ParseSchema+combine.swift +++ b/Sources/ParseSwift/Types/ParseSchema+combine.swift @@ -17,6 +17,7 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -32,6 +33,7 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func createPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -47,6 +49,7 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ func updatePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -63,6 +66,7 @@ public extension ParseSchema { - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. + - requires: `.useMasterKey` has to be available. */ func purgePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -80,6 +84,7 @@ public extension ParseSchema { desires a different policy, it should be inserted in `options`. - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` currently contains objects, run `purge()` first. + - requires: `.useMasterKey` has to be available. */ func deletePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index c29f3af37..66797374a 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -387,11 +387,13 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { var options = options + options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try fetchCommand() @@ -431,11 +433,13 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ public func create(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { var options = options + options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try createCommand() @@ -463,11 +467,13 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ public func update(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { var options = options + options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try updateCommand() @@ -519,6 +525,7 @@ extension ParseSchema { - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ public func purge( options: API.Options = [], @@ -526,6 +533,7 @@ extension ParseSchema { completion: @escaping (Result) -> Void ) { var options = options + options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try deleteCommand().executeAsync(options: options, @@ -563,6 +571,7 @@ extension ParseSchema { currently contains objects, run `purge()` first. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. + - requires: `.useMasterKey` has to be available. */ public func delete( options: API.Options = [], @@ -570,6 +579,7 @@ extension ParseSchema { completion: @escaping (Result) -> Void ) { var options = options + options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try deleteCommand().executeAsync(options: options, @@ -622,3 +632,21 @@ extension ParseSchema { } } } + +// MARK: CustomDebugStringConvertible +extension ParseSchema: CustomDebugStringConvertible { + public var debugDescription: String { + guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), + let descriptionString = String(data: descriptionData, encoding: .utf8) else { + return "ParseSchema ()" + } + return "ParseSchema (\(descriptionString))" + } +} + +// MARK: CustomStringConvertible +extension ParseSchema: CustomStringConvertible { + public var description: String { + debugDescription + } +} From 9fd246b80f6e03cb7ce7185e99d35ef9499bfed4 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 23 May 2022 09:15:57 -0400 Subject: [PATCH 15/55] fix CLP --- .../20 - Schema.xcplaygroundpage/Contents.swift | 2 -- Sources/ParseSwift/Types/ParseCLP.swift | 13 +++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index b2ee941c8..727d94247 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -52,8 +52,6 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: clp) type: .number, options: ParseFieldOptions(required: false, defauleValue: 0)) -print(gameScoreSchema) - //: Now lets create the schema on the server. gameScoreSchema.create { result in switch result { diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 9564a0a21..fa8ccb569 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -37,8 +37,13 @@ public struct ParseCLP: Codable, Equatable { func setAccess(_ key: WritableKeyPath, for entity: String, to allow: Bool) -> Self { + let allowed: Bool? = allow ? allow : nil var mutableCLP = self - mutableCLP[keyPath: key]?[entity] = allow + if mutableCLP[keyPath: key] != nil { + mutableCLP[keyPath: key]?[entity] = allowed + } else if let allowed = allowed { + mutableCLP[keyPath: key] = [entity: allowed] + } return mutableCLP } @@ -62,7 +67,11 @@ public struct ParseCLP: Codable, Equatable { fields: [String], for entity: String) -> Self { var mutableCLP = self - mutableCLP[keyPath: key]?[entity] = fields + if mutableCLP[keyPath: key] != nil { + mutableCLP[keyPath: key]?[entity] = fields + } else { + mutableCLP[keyPath: key] = [entity: fields] + } return mutableCLP } } From d15abe4b9745e8c6f829b3d34282c29ba36c2e6c Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 23 May 2022 14:03:16 -0400 Subject: [PATCH 16/55] make all CLP methods public --- .../Contents.swift | 6 +- Sources/ParseSwift/Types/ParseCLP.swift | 68 +++++++++---------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 727d94247..e03476918 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -18,6 +18,7 @@ struct GameScore2: ParseObject { //: Your own properties. var points: Int? + var data: ParseBytes? //: Implement your own version of merge func merge(with object: Self) throws -> Self { @@ -50,7 +51,10 @@ let clp = ParseCLP(requireAuthentication: false, publicAccess: true) var gameScoreSchema = ParseSchema(classLevelPermissions: clp) .addField("points", type: .number, - options: ParseFieldOptions(required: false, defauleValue: 0)) + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("data", + type: .bytes, + options: ParseFieldOptions(required: false, defauleValue: nil)) //: Now lets create the schema on the server. gameScoreSchema.create { result in diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index fa8ccb569..babdcf795 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -28,6 +28,40 @@ public struct ParseCLP: Codable, Equatable { /// An empty CLP. public init() { } +} + +// MARK: Default Implementation +public extension ParseCLP { + + init(requireAuthentication: Bool, publicAccess: Bool) { + let clp = setRequiresAuthenticationWriteAccess(requireAuthentication) + .setRequiresAuthenticationReadAccess(requireAuthentication) + .setPublicWriteAccess(publicAccess) + .setPublicReadAccess(publicAccess) + self = clp + } + + init(objectId: String, canAddFied: Bool = false) { + let clp = setWriteAccess(objectId, + to: true, + canAddField: canAddFied) + .setReadAccess(objectId, to: true) + self = clp + } + + init(user: U, canAddFied: Bool = false) throws where U: ParseUser { + let objectId = try user.toPointer().objectId + self.init(objectId: objectId, canAddFied: canAddFied) + } + + init(user: Pointer, canAddFied: Bool = false) where U: ParseUser { + self.init(objectId: user.objectId, canAddFied: canAddFied) + } + + init(role: R, canAddFied: Bool = false) throws where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + self.init(objectId: roleNameAccess, canAddFied: canAddFied) + } func getAccess(_ key: KeyPath, for entity: String) -> Bool { @@ -76,40 +110,6 @@ public struct ParseCLP: Codable, Equatable { } } -// MARK: Default Implementation -public extension ParseCLP { - - init(requireAuthentication: Bool, publicAccess: Bool) { - let clp = setRequiresAuthenticationWriteAccess(requireAuthentication) - .setRequiresAuthenticationReadAccess(requireAuthentication) - .setPublicWriteAccess(publicAccess) - .setPublicReadAccess(publicAccess) - self = clp - } - - init(objectId: String, canAddFied: Bool = false) { - let clp = setWriteAccess(objectId, - to: true, - canAddField: canAddFied) - .setReadAccess(objectId, to: true) - self = clp - } - - init(user: U, canAddFied: Bool = false) throws where U: ParseUser { - let objectId = try user.toPointer().objectId - self.init(objectId: objectId, canAddFied: canAddFied) - } - - init(user: Pointer, canAddFied: Bool = false) where U: ParseUser { - self.init(objectId: user.objectId, canAddFied: canAddFied) - } - - init(role: R, canAddFied: Bool = false) throws where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - self.init(objectId: roleNameAccess, canAddFied: canAddFied) - } -} - // MARK: Protected public extension ParseCLP { From 45e386d4ccb5e991c72b177af4fdf59ba9514c50 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 23 May 2022 14:37:00 -0400 Subject: [PATCH 17/55] improve docs --- .../Protocols/QueryObservable.swift | 4 ++- .../ParseSwift/Types/ParseFile+async.swift | 4 ++- .../ParseSwift/Types/ParseFile+combine.swift | 4 ++- Sources/ParseSwift/Types/ParseFile.swift | 8 +++-- .../ParseSwift/Types/ParseSchema+async.swift | 20 +++++++++--- .../Types/ParseSchema+combine.swift | 20 +++++++++--- Sources/ParseSwift/Types/ParseSchema.swift | 24 ++++++++++---- Sources/ParseSwift/Types/Query+async.swift | 16 +++++++--- Sources/ParseSwift/Types/Query+combine.swift | 16 +++++++--- Sources/ParseSwift/Types/Query.swift | 32 ++++++++++++++----- 10 files changed, 111 insertions(+), 37 deletions(-) diff --git a/Sources/ParseSwift/Protocols/QueryObservable.swift b/Sources/ParseSwift/Protocols/QueryObservable.swift index d0a6017ce..5d8ae0891 100644 --- a/Sources/ParseSwift/Protocols/QueryObservable.swift +++ b/Sources/ParseSwift/Protocols/QueryObservable.swift @@ -63,7 +63,9 @@ public protocol QueryObservable: ObservableObject { /** Executes an aggregate query *asynchronously* and updates the view model when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - warning: This hasn't been tested thoroughly. diff --git a/Sources/ParseSwift/Types/ParseFile+async.swift b/Sources/ParseSwift/Types/ParseFile+async.swift index 1092d8e23..5f03ee5df 100644 --- a/Sources/ParseSwift/Types/ParseFile+async.swift +++ b/Sources/ParseSwift/Types/ParseFile+async.swift @@ -91,7 +91,9 @@ public extension ParseFile { /** Deletes the file from the Parse Server. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. */ diff --git a/Sources/ParseSwift/Types/ParseFile+combine.swift b/Sources/ParseSwift/Types/ParseFile+combine.swift index 4b6b33496..dc996786a 100644 --- a/Sources/ParseSwift/Types/ParseFile+combine.swift +++ b/Sources/ParseSwift/Types/ParseFile+combine.swift @@ -86,7 +86,9 @@ public extension ParseFile { /** Deletes the file from the Parse Server. Publishes when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. */ diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index a3d76abb6..fab0a4e56 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -158,7 +158,9 @@ extension ParseFile { extension ParseFile { /** Deletes the file from the Parse cloud. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after synchronous completion. - throws: A `ParseError` if there was an issue deleting the file. Otherwise it was successful. @@ -177,7 +179,9 @@ extension ParseFile { /** Deletes the file from the Parse cloud. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: A `ParseError` if there was an issue deleting the file. Otherwise it was successful. */ diff --git a/Sources/ParseSwift/Types/ParseSchema+async.swift b/Sources/ParseSwift/Types/ParseSchema+async.swift index fc9e05b4f..fb238fcc1 100644 --- a/Sources/ParseSwift/Types/ParseSchema+async.swift +++ b/Sources/ParseSwift/Types/ParseSchema+async.swift @@ -17,7 +17,9 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func fetch(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -33,7 +35,9 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func create(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -49,7 +53,9 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func update(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -66,7 +72,9 @@ public extension ParseSchema { - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func purge(options: API.Options = []) async throws { let result = try await withCheckedThrowingContinuation { continuation in @@ -87,7 +95,9 @@ public extension ParseSchema { currently contains objects, run `purge()` first. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func delete(options: API.Options = []) async throws { let result = try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseSchema+combine.swift b/Sources/ParseSwift/Types/ParseSchema+combine.swift index 7fc90fc39..83c5f78c7 100644 --- a/Sources/ParseSwift/Types/ParseSchema+combine.swift +++ b/Sources/ParseSwift/Types/ParseSchema+combine.swift @@ -17,7 +17,9 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -33,7 +35,9 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func createPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -49,7 +53,9 @@ public extension ParseSchema { - returns: A publisher that eventually produces a single value and then finishes or fails. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func updatePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -66,7 +72,9 @@ public extension ParseSchema { - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func purgePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { @@ -84,7 +92,9 @@ public extension ParseSchema { desires a different policy, it should be inserted in `options`. - warning: This can only be used on a `ParseSchema` without objects. If the `ParseSchema` currently contains objects, run `purge()` first. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ func deletePublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 66797374a..bbc777509 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -10,7 +10,9 @@ import Foundation /** `ParseSchema` is used for handeling your schemas. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public struct ParseSchema: ParseType, Decodable { @@ -387,7 +389,9 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -433,7 +437,9 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public func create(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -467,7 +473,9 @@ extension ParseSchema { It should have the following argument signature: `(Result)`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public func update(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -525,7 +533,9 @@ extension ParseSchema { - warning: This will delete all objects for this `ParseSchema` and cannot be reversed. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public func purge( options: API.Options = [], @@ -571,7 +581,9 @@ extension ParseSchema { currently contains objects, run `purge()` first. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. */ public func delete( options: API.Options = [], diff --git a/Sources/ParseSwift/Types/Query+async.swift b/Sources/ParseSwift/Types/Query+async.swift index abeda2663..9262d5430 100644 --- a/Sources/ParseSwift/Types/Query+async.swift +++ b/Sources/ParseSwift/Types/Query+async.swift @@ -178,7 +178,9 @@ public extension Query { /** Executes an aggregate query *asynchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. @@ -195,7 +197,9 @@ public extension Query { /** Query plan information for executing an aggregate query *asynchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -222,7 +226,9 @@ public extension Query { /** Executes a distinct query *asynchronously* and returns unique values when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. @@ -239,7 +245,9 @@ public extension Query { /** Query plan information for executing a distinct query *asynchronously* and returns unique values when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper diff --git a/Sources/ParseSwift/Types/Query+combine.swift b/Sources/ParseSwift/Types/Query+combine.swift index 8109561a4..2a61b2a07 100644 --- a/Sources/ParseSwift/Types/Query+combine.swift +++ b/Sources/ParseSwift/Types/Query+combine.swift @@ -172,7 +172,9 @@ public extension Query { /** Executes an aggregate query *asynchronously* and publishes when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. @@ -188,7 +190,9 @@ public extension Query { /** Query plan information for executing an aggregate query *asynchronously* and publishes when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -214,7 +218,9 @@ public extension Query { /** Executes a distinct query *asynchronously* and publishes unique values when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. @@ -230,7 +236,9 @@ public extension Query { /** Query plan information for executing a distinct query *asynchronously* and publishes unique values when complete. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper diff --git a/Sources/ParseSwift/Types/Query.swift b/Sources/ParseSwift/Types/Query.swift index eba0d5a94..2d0545bc5 100644 --- a/Sources/ParseSwift/Types/Query.swift +++ b/Sources/ParseSwift/Types/Query.swift @@ -845,7 +845,9 @@ extension Query: Queryable { /** Executes an aggregate query *synchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. @@ -885,7 +887,9 @@ extension Query: Queryable { /** Executes an aggregate query *asynchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of `.main`. @@ -939,7 +943,9 @@ extension Query: Queryable { /** Query plan information for executing an aggregate query *synchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -991,7 +997,9 @@ extension Query: Queryable { /** Query plan information for executing an aggregate query *asynchronously*. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -1058,7 +1066,9 @@ extension Query: Queryable { /** Executes an aggregate query *synchronously* and calls the given. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. @@ -1078,7 +1088,9 @@ extension Query: Queryable { /** Executes a distinct query *asynchronously* and returns unique values. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of `.main`. @@ -1107,7 +1119,9 @@ extension Query: Queryable { /** Query plan information for executing an aggregate query *synchronously* and calls the given. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -1141,7 +1155,9 @@ extension Query: Queryable { /** Query plan information for executing a distinct query *asynchronously* and returns unique values. - - requires: `.useMasterKey` has to be available. + - requires: `.useMasterKey` has to be available. It is recommended to only + use the master key in server-side applications where the key is kept secure and not + exposed to the public. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for MongoDB and PostgreSQL. One way around this is to use a type-erased wrapper From 7d02bd7a207772fe1bfa4728bb858fee81a8d924 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 26 May 2022 18:12:20 -0400 Subject: [PATCH 18/55] improve CLP --- .../Contents.swift | 3 + Sources/ParseSwift/Types/ParseCLP.swift | 671 ++++++------------ 2 files changed, 233 insertions(+), 441 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index e03476918..0a1b1c77d 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -46,6 +46,9 @@ extension GameScore2 { //: First lets create a new CLP for the new schema. let clp = ParseCLP(requireAuthentication: false, publicAccess: true) + .setAccessPublic(\.count, to: false) + .setAccessRequiresAuthentication(\.count, to: true) + .setAccessRequiresAuthentication(\.addField, to: true) //: Next we use the CLP to create the new schema and add fields to it. var gameScoreSchema = ParseSchema(classLevelPermissions: clp) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index babdcf795..ded6b29df 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -8,44 +8,80 @@ import Foundation +/// Class Level Permissions for `ParseSchema`. public struct ParseCLP: Codable, Equatable { - public internal(set) var get: [String: Bool]? - public internal(set) var find: [String: Bool]? - public internal(set) var count: [String: Bool]? - public internal(set) var create: [String: Bool]? - public internal(set) var update: [String: Bool]? - public internal(set) var delete: [String: Bool]? - public internal(set) var addField: [String: Bool]? - public internal(set) var protectedFields: [String: [String]]? - public internal(set) var readUserFields: [String]? - public internal(set) var writeUserFields: [String]? - - enum Access: String, Codable { + public var get: [String: Bool]? + public var find: [String: Bool]? + public var count: [String: Bool]? + public var create: [String: Bool]? + public var update: [String: Bool]? + public var delete: [String: Bool]? + public var addField: [String: Bool]? + public var protectedFields: [String: [String]]? + public var readUserFields: [String]? + public var writeUserFields: [String]? + + enum Access: String { case requiresAuthentication case publicScope = "*" } - /// An empty CLP. + /// Creates an empty CLP type. public init() { } + + func hasAccess(_ keyPath: KeyPath, + for entity: String) -> Bool { + self[keyPath: keyPath]?[entity] ?? false + } + + func setAccess(_ keyPath: WritableKeyPath, + to allow: Bool, + for entity: String) -> Self { + let allowed: Bool? = allow ? allow : nil + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = allowed + } else if let allowed = allowed { + mutableCLP[keyPath: keyPath] = [entity: allowed] + } + return mutableCLP + } + + func getProtected(_ keyPath: KeyPath, + for entity: String) -> [String] { + self[keyPath: keyPath]?[entity] ?? [] + } + + func setProtected(_ keyPath: WritableKeyPath, + fields: [String], + for entity: String) -> Self { + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = fields + } else { + mutableCLP[keyPath: keyPath] = [entity: fields] + } + return mutableCLP + } } // MARK: Default Implementation public extension ParseCLP { init(requireAuthentication: Bool, publicAccess: Bool) { - let clp = setRequiresAuthenticationWriteAccess(requireAuthentication) - .setRequiresAuthenticationReadAccess(requireAuthentication) - .setPublicWriteAccess(publicAccess) - .setPublicReadAccess(publicAccess) + let clp = setWriteAccessRequiresAuthentication(requireAuthentication) + .setReadAccessRequiresAuthentication(requireAuthentication) + .setWriteAccessPublic(publicAccess) + .setReadAccessPublic(publicAccess) self = clp } init(objectId: String, canAddFied: Bool = false) { - let clp = setWriteAccess(objectId, - to: true, + let clp = setWriteAccess(true, + objectId: objectId, canAddField: canAddFied) - .setReadAccess(objectId, to: true) + .setReadAccess(true, objectId: objectId) self = clp } @@ -63,49 +99,85 @@ public extension ParseCLP { self.init(objectId: roleNameAccess, canAddFied: canAddFied) } - func getAccess(_ key: KeyPath, - for entity: String) -> Bool { - self[keyPath: key]?[entity] ?? false + /** + Checks if get/find/count/create/update/delete/addField actions currently have public access. + - parameter keyPath: Any of the following keyPaths that represent an + action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - returns: **true** if access is allowed, **false** otherwise. + */ + func hasAccessPublic(_ keyPath: KeyPath) throws -> Bool { + hasAccess(keyPath, for: Access.publicScope.rawValue) } - func setAccess(_ key: WritableKeyPath, - for entity: String, - to allow: Bool) -> Self { - let allowed: Bool? = allow ? allow : nil - var mutableCLP = self - if mutableCLP[keyPath: key] != nil { - mutableCLP[keyPath: key]?[entity] = allowed - } else if let allowed = allowed { - mutableCLP[keyPath: key] = [entity: allowed] - } - return mutableCLP + /** + Checks if get/find/count/create/update/delete/addField actions currently requires authentication to access. + - parameter keyPath: Any of the following keyPaths that represent an + action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - returns: **true** if access is allowed, **false** otherwise. + - warning: Requires Parse Server 2.3.0+. + */ + func hasAccessRequiresAuthentication(_ keyPath: KeyPath) throws -> Bool { + hasAccess(keyPath, for: Access.requiresAuthentication.rawValue) } - func getUser(_ key: KeyPath) -> [String] { - self[keyPath: key] ?? [] + func hasAccess(_ keyPath: KeyPath, + for user: U) throws -> Bool where U: ParseUser { + let objectId = try user.toPointer().objectId + return hasAccess(keyPath, for: objectId) } - func setUser(_ key: WritableKeyPath, - fields: [String]) -> Self { - var mutableCLP = self - mutableCLP[keyPath: key] = fields - return mutableCLP + func hasAccess(_ keyPath: KeyPath, + for user: Pointer) throws -> Bool where U: ParseUser { + hasAccess(keyPath, for: user.objectId) } - func getProtected(_ key: KeyPath, - for entity: String) -> [String] { - self[keyPath: key]?[entity] ?? [] + func hasAccess(_ keyPath: KeyPath, + for role: R) throws -> Bool where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return hasAccess(keyPath, for: roleNameAccess) } - func setProtected(_ key: WritableKeyPath, - fields: [String], - for entity: String) -> Self { + func setAccessPublic(_ keyPath: WritableKeyPath, + to allow: Bool) -> Self { + setAccess(keyPath, to: allow, for: Access.publicScope.rawValue) + } + + /** + - warning: Requires Parse Server 2.3.0+. + */ + func setAccessRequiresAuthentication(_ keyPath: WritableKeyPath, + to allow: Bool) -> Self { + setAccess(keyPath, to: allow, for: Access.requiresAuthentication.rawValue) + } + + func setAccess(_ keyPath: WritableKeyPath, + to allow: Bool, + for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setAccess(keyPath, to: allow, for: objectId) + } + + func setAccess(_ keyPath: WritableKeyPath, + to allow: Bool, + for user: Pointer) -> Self where U: ParseUser { + setAccess(keyPath, to: allow, for: user.objectId) + } + + func setAccess(_ keyPath: WritableKeyPath, + to allow: Bool, + for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setAccess(keyPath, to: allow, for: roleNameAccess) + } + + func getUser(_ keyPath: KeyPath) -> [String] { + self[keyPath: keyPath] ?? [] + } + + func setUser(_ keyPath: WritableKeyPath, + fields: [String]) -> Self { var mutableCLP = self - if mutableCLP[keyPath: key] != nil { - mutableCLP[keyPath: key]?[entity] = fields - } else { - mutableCLP[keyPath: key] = [entity: fields] - } + mutableCLP[keyPath: keyPath] = fields return mutableCLP } } @@ -114,12 +186,12 @@ public extension ParseCLP { public extension ParseCLP { /** - Get the protected fields for the given user objectId. + Get the protected fields for the given `ParseUser` objectId. - - parameter objectId: The user objectId access to check. + - parameter user: The `ParseUser` objectId access to check. - returns: The protected fields. */ - func getProtected(_ objectId: String) -> [String] { + func getProtectedFields(_ objectId: String) -> [String] { getProtected(\.protectedFields, for: objectId) } @@ -130,9 +202,9 @@ public extension ParseCLP { - returns: The protected fields. - throws: An error of type `ParseError`. */ - func getProtected(_ user: U) throws -> [String] where U: ParseUser { + func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser { let objectId = try user.toPointer().objectId - return getProtected(objectId) + return getProtectedFields(objectId) } /** @@ -141,8 +213,8 @@ public extension ParseCLP { - parameter user: The `ParseUser` access to check. - returns: The protected fields. */ - func getProtected(_ user: Pointer) -> [String] where U: ParseUser { - getProtected(user.objectId) + func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser { + getProtectedFields(user.objectId) } /** @@ -152,19 +224,20 @@ public extension ParseCLP { - returns: The protected fields. - throws: An error of type `ParseError`. */ - func getProtected(_ role: R) throws -> [String] where R: ParseRole { + func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return getProtected(roleNameAccess) + return getProtectedFields(roleNameAccess) } /** - Set whether the given user objectId is allowed to retrieve fields from this class. + Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. - - parameter for: The user objectId to provide/restrict access to. + - parameter for: The `ParseUser` objectId to provide/restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. */ - func setProtected(_ fields: [String], for objectId: String) -> Self { + func setProtectedFields(_ fields: [String], for objectId: String) -> Self { setProtected(\.protectedFields, fields: fields, for: objectId) } @@ -176,9 +249,9 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setProtected(_ fields: [String], for user: U) throws -> Self where U: ParseUser { + func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setProtected(fields, for: objectId) + return setProtectedFields(fields, for: objectId) } /** @@ -188,8 +261,8 @@ public extension ParseCLP { - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtected(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { - setProtected(fields, for: user.objectId) + func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { + setProtectedFields(fields, for: user.objectId) } /** @@ -200,9 +273,9 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setProtected(_ fields: [String], for role: R) throws -> Self where R: ParseRole { + func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setProtected(fields, for: roleNameAccess) + return setProtectedFields(fields, for: roleNameAccess) } } @@ -252,302 +325,16 @@ public extension ParseCLP { } } -// MARK: RequiresAuthenication -public extension ParseCLP { - - /** - Check whether **get** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesGetRequireAuthentication() -> Bool { - getAccess(\.get, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **find** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesFindRequireAuthentication() -> Bool { - getAccess(\.find, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **count** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesCountRequireAuthentication() -> Bool { - getAccess(\.count, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **create** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesCreateRequireAuthentication() -> Bool { - getAccess(\.create, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **update** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesUpdateRequireAuthentication() -> Bool { - getAccess(\.update, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **delete** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesDeleteRequireAuthentication() -> Bool { - getAccess(\.delete, for: Access.requiresAuthentication.rawValue) - } - - /** - Check whether **addField** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesAddFieldRequireAuthentication() -> Bool { - getAccess(\.addField, for: Access.requiresAuthentication.rawValue) - } - - /** - Sets whether **get** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setGetRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.get, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **find** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setFindRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.find, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **count** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setCountRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.count, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **create** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setCreateRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.create, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **update** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setUpdateRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.update, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **delete** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setDeleteRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.delete, for: Access.requiresAuthentication.rawValue, to: allow) - } - - /** - Sets whether **addField** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setAddFieldRequiresAuthentication(_ allow: Bool) -> Self { - setAccess(\.addField, for: Access.requiresAuthentication.rawValue, to: allow) - } -} - -// MARK: PublicAccess -public extension ParseCLP { - - /** - Check whether **get** has public access for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesGetHavePublicAccess() -> Bool { - getAccess(\.get, for: Access.publicScope.rawValue) - } - - /** - Check whether **find** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesFindHavePublicAccess() -> Bool { - getAccess(\.find, for: Access.publicScope.rawValue) - } - - /** - Check whether **count** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesCountHavePublicAccess() -> Bool { - getAccess(\.count, for: Access.publicScope.rawValue) - } - - /** - Check whether **create** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesCreateHavePublicAccess() -> Bool { - getAccess(\.create, for: Access.publicScope.rawValue) - } - - /** - Check whether **update** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesUpdateHavePublicAccess() -> Bool { - getAccess(\.update, for: Access.publicScope.rawValue) - } - - /** - Check whether **delete** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesDeleteHavePublicAccess() -> Bool { - getAccess(\.delete, for: Access.publicScope.rawValue) - } - - /** - Check whether **addField** requires authentication for this class. - - - returns: **true** if access is allowed, **false** otherwise. - */ - func doesAddFieldHavePublicAccess() -> Bool { - getAccess(\.addField, for: Access.publicScope.rawValue) - } - - /** - Sets whether **get** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setGetPublicAccess(_ allow: Bool) -> Self { - setAccess(\.get, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **find** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setFindPublicAccess(_ allow: Bool) -> Self { - setAccess(\.find, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **count** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setCountPublicAccess(_ allow: Bool) -> Self { - setAccess(\.count, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **create** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setCreatePublicAccess(_ allow: Bool) -> Self { - setAccess(\.create, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **update** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setUpdatePublicAccess(_ allow: Bool) -> Self { - setAccess(\.update, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **delete** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setDeletePublicAccess(_ allow: Bool) -> Self { - setAccess(\.delete, for: Access.publicScope.rawValue, to: allow) - } - - /** - Sets whether **addField** requires authentication for this class. - - - parameter allow: **true** if access should be allowed, **false** otherwise. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setAddFieldPublicAccess(_ allow: Bool) -> Self { - setAccess(\.addField, for: Access.publicScope.rawValue, to: allow) - } -} - // MARK: WriteAccess public extension ParseCLP { - internal func setWrite(_ entity: String, - to allow: Bool, - can addField: Bool) -> Self { - var updatedCLP = self - .setAccess(\.create, for: entity, to: allow) - .setAccess(\.update, for: entity, to: allow) - .setAccess(\.delete, for: entity, to: allow) + internal func hasWriteAccess(_ entity: String, + check addField: Bool) -> Bool { + let access = hasAccess(\.create, for: entity) + && hasAccess(\.update, for: entity) + && hasAccess(\.delete, for: entity) if addField { - updatedCLP = updatedCLP.setAccess(\.addField, for: entity, to: allow) - } - return updatedCLP - } - - internal func getWrite(_ entity: String, check addField: Bool) -> Bool { - let access = getAccess(\.create, for: entity) - && getAccess(\.update, for: entity) - && getAccess(\.delete, for: entity) - if addField { - return access && getAccess(\.addField, for: entity) + return access && hasAccess(\.addField, for: entity) } return access } @@ -558,8 +345,8 @@ public extension ParseCLP { Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. */ - func doesWriteRequireAuthentication(_ checkAddField: Bool = false) -> Bool { - getWrite(Access.requiresAuthentication.rawValue, check: checkAddField) + func hasWriteAccessRequiresAuthentication(_ checkAddField: Bool = false) -> Bool { + hasWriteAccess(Access.requiresAuthentication.rawValue, check: checkAddField) } /** @@ -568,22 +355,23 @@ public extension ParseCLP { Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. */ - func doesWriteHavePublicAccess(_ checkAddField: Bool = false) -> Bool { - getWrite(Access.publicScope.rawValue, check: checkAddField) + func hasWriteAccessPublic(_ checkAddField: Bool = false) -> Bool { + hasWriteAccess(Access.publicScope.rawValue, check: checkAddField) } /** - Check whether the user objectId has access to write to this class. - - parameter objectId: The user objectId to check. + Check whether the `ParseUser` objectId has access to write to this class. + - parameter objectId: The `ParseUser` objectId to check. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - returns: **true** if authentication is required, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. + - throws: An error of type `ParseError`. */ - func doesHaveWriteAccess(_ objectId: String, - checkAddField: Bool = false) -> Bool { - getWrite(objectId, check: checkAddField) + func hasWriteAccess(_ objectId: String, + checkAddField: Bool = false) -> Bool { + hasWriteAccess(objectId, check: checkAddField) } /** @@ -595,9 +383,9 @@ public extension ParseCLP { - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ - func doesHaveWriteAccess(_ user: Pointer, - checkAddField: Bool = false) -> Bool where U: ParseUser { - doesHaveWriteAccess(user.objectId, checkAddField: checkAddField) + func hasWriteAccess(_ user: Pointer, + checkAddField: Bool = false) -> Bool where U: ParseUser { + hasWriteAccess(user.objectId, checkAddField: checkAddField) } /** @@ -610,10 +398,10 @@ public extension ParseCLP { have access if they aare apart of a `ParseRole` that has access. - throws: An error of type `ParseError`. */ - func doesHaveWriteAccess(_ user: U, - checkAddField: Bool = false) throws -> Bool where U: ParseUser { + func hasWriteAccess(_ user: U, + checkAddField: Bool = false) throws -> Bool where U: ParseUser { let objectId = try user.toPointer().objectId - return doesHaveWriteAccess(objectId, checkAddField: checkAddField) + return hasWriteAccess(objectId, checkAddField: checkAddField) } /** @@ -626,10 +414,10 @@ public extension ParseCLP { have access if they aare apart of a `ParseRole` that has access. - throws: An error of type `ParseError`. */ - func doesHaveWriteAccess(_ role: R, - checkAddField: Bool = false) throws -> Bool where R: ParseRole { + func hasWriteAccess(_ role: R, + checkAddField: Bool = false) throws -> Bool where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return doesHaveWriteAccess(roleNameAccess, checkAddField: checkAddField) + return hasWriteAccess(roleNameAccess, checkAddField: checkAddField) } /** @@ -640,9 +428,9 @@ public extension ParseCLP { Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setRequiresAuthenticationWriteAccess(_ allow: Bool, + func setWriteAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setWrite(Access.requiresAuthentication.rawValue, to: allow, can: addField) + setWriteAccess(allow, objectId: Access.requiresAuthentication.rawValue, canAddField: addField) } /** @@ -653,24 +441,31 @@ public extension ParseCLP { Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setPublicWriteAccess(_ allow: Bool, + func setWriteAccessPublic(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setWrite(Access.publicScope.rawValue, to: allow, can: addField) + setWriteAccess(allow, objectId: Access.publicScope.rawValue, canAddField: addField) } /** - Sets whether the given user objectId is allowed to create/update/delete/addField to this class. + Sets whether the given `ParseUser` objectId is allowed to create/update/delete/addField to this class. - - parameter objectId: The user objectId to provide/restrict access to. + - parameter objectId: The `ParseUser` objectId to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteAccess(_ objectId: String, - to allow: Bool, + func setWriteAccess(_ allow: Bool, + objectId: String, canAddField addField: Bool = false) -> Self { - setWrite(objectId, to: allow, can: addField) + var updatedCLP = self + .setAccess(\.create, to: allow, for: objectId) + .setAccess(\.update, to: allow, for: objectId) + .setAccess(\.delete, to: allow, for: objectId) + if addField { + updatedCLP = updatedCLP.setAccess(\.addField, to: allow, for: objectId) + } + return updatedCLP } /** @@ -683,11 +478,11 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setWriteAccess(_ user: U, - to allow: Bool, + func setWriteAccess(_ allow: Bool, + user: U, canAddField addField: Bool = false) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setWriteAccess(objectId, to: allow, canAddField: addField) + return setWriteAccess(allow, objectId: objectId, canAddField: addField) } /** @@ -699,10 +494,10 @@ public extension ParseCLP { Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteAccess(_ user: Pointer, - to allow: Bool, + func setWriteAccess(_ allow: Bool, + user: Pointer, canAddField addField: Bool = false) -> Self where U: ParseUser { - setWriteAccess(user.objectId, to: allow, canAddField: addField) + setWriteAccess(allow, objectId: user.objectId, canAddField: addField) } /** @@ -715,56 +510,44 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setWriteAccess(_ role: R, - to allow: Bool, + func setWriteAccess(_ allow: Bool, + role: R, canAddField addField: Bool = false) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setWriteAccess(roleNameAccess, to: allow, canAddField: addField) + return setWriteAccess(allow, objectId: roleNameAccess, canAddField: addField) } } // MARK: ReadAccess public extension ParseCLP { - internal func setRead(_ entity: String, to allow: Bool) -> Self { - let updatedCLP = self - .setAccess(\.get, for: entity, to: allow) - .setAccess(\.find, for: entity, to: allow) - .setAccess(\.count, for: entity, to: allow) - return updatedCLP - } - - internal func getRead(_ entity: String) -> Bool { - getAccess(\.get, for: entity) - && getAccess(\.find, for: entity) - && getAccess(\.count, for: entity) - } - /** Check whether authentication is required to read from this class. - returns: **true** if authentication is required, **false** otherwise. */ - func doesReadRequireAuthentication() -> Bool { - getRead(Access.requiresAuthentication.rawValue) + func hasReadAccessRequiresAuthentication() -> Bool { + hasReadAccess(Access.requiresAuthentication.rawValue) } /** Check whether the public has access to read from this class. - returns: **true** if authentication is required, **false** otherwise. */ - func doesReadHavePublicAccess() -> Bool { - getRead(Access.publicScope.rawValue) + func hasReadAccessPublic() -> Bool { + hasReadAccess(Access.publicScope.rawValue) } /** - Check whether the user objectId has access to read from this class. - - parameter objectId: The user objectId to check. + Check whether the `ParseUser` objectId has access to read from this class. + - parameter objectId: The `ParseUser` objectId to check. - returns: **true** if authentication is required, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ - func doesHaveReadAccess(_ objectId: String) -> Bool { - getRead(objectId) + func hasReadAccess(_ objectId: String) -> Bool { + hasAccess(\.get, for: objectId) + && hasAccess(\.find, for: objectId) + && hasAccess(\.count, for: objectId) } /** @@ -774,8 +557,8 @@ public extension ParseCLP { - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ - func doesHaveReadAccess(_ user: Pointer) -> Bool where U: ParseUser { - doesHaveReadAccess(user.objectId) + func hasReadAccess(_ user: Pointer) -> Bool where U: ParseUser { + hasReadAccess(user.objectId) } /** @@ -786,9 +569,9 @@ public extension ParseCLP { - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ - func doesHaveReadAccess(_ user: U) throws -> Bool where U: ParseUser { + func hasReadAccess(_ user: U) throws -> Bool where U: ParseUser { let objectId = try user.toPointer().objectId - return doesHaveReadAccess(objectId) + return hasReadAccess(objectId) } /** @@ -799,9 +582,9 @@ public extension ParseCLP { - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still have access if they aare apart of a `ParseRole` that has access. */ - func doesHaveReadAccess(_ role: R) throws -> Bool where R: ParseRole { + func hasReadAccess(_ role: R) throws -> Bool where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return doesHaveReadAccess(roleNameAccess) + return hasReadAccess(roleNameAccess) } /** @@ -812,9 +595,9 @@ public extension ParseCLP { Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setRequiresAuthenticationReadAccess(_ allow: Bool, + func setReadAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setRead(Access.requiresAuthentication.rawValue, to: allow) + setReadAccess(allow, objectId: Access.requiresAuthentication.rawValue) } /** @@ -823,19 +606,24 @@ public extension ParseCLP { - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setPublicReadAccess(_ allow: Bool) -> Self { - setRead(Access.publicScope.rawValue, to: allow) + func setReadAccessPublic(_ allow: Bool) -> Self { + setReadAccess(allow, objectId: Access.publicScope.rawValue) } /** - Sets whether the given user objectId is allowed to get/find/count this class. + Sets whether the given `ParseUser` is allowed to get/find/count this class. - - parameter objectId: The user objectId to provide/restrict access to. + - parameter objectId: The `ParseUser` pointer to provide/restrict access to. - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadAccess(_ objectId: String, to allow: Bool) -> Self { - setRead(objectId, to: allow) + func setReadAccess(_ allow: Bool, + objectId: String) -> Self { + let updatedCLP = self + .setAccess(\.get, to: allow, for: objectId) + .setAccess(\.find, to: allow, for: objectId) + .setAccess(\.count, to: allow, for: objectId) + return updatedCLP } /** @@ -846,9 +634,10 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setReadAccess(_ user: U, to allow: Bool) throws -> Self where U: ParseUser { + func setReadAccess(_ allow: Bool, + user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setReadAccess(objectId, to: allow) + return setReadAccess(allow, objectId: objectId) } /** @@ -858,9 +647,9 @@ public extension ParseCLP { - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadAccess(_ user: Pointer, - to allow: Bool) -> Self where U: ParseUser { - return setReadAccess(user.objectId, to: allow) + func setReadAccess(_ allow: Bool, + user: Pointer) -> Self where U: ParseUser { + return setReadAccess(allow, objectId: user.objectId) } /** @@ -871,9 +660,9 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setReadAccess(_ role: R, to allow: Bool) throws -> Self where R: ParseRole { + func setReadAccess(_ allow: Bool, role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setReadAccess(roleNameAccess, to: allow) + return setReadAccess(allow, objectId: roleNameAccess) } } From ee6753288e6a95a5b6a1b67cdc088a6a0e88699a Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Fri, 27 May 2022 09:09:00 -0400 Subject: [PATCH 19/55] CLP protocol start --- ParseSwift.playground/Sources/Common.swift | 4 +- ParseSwift.xcodeproj/project.pbxproj | 10 + .../ParseClassLevelPermisioinable.swift | 204 ++++++++++++++++++ Sources/ParseSwift/Types/ParseCLP.swift | 172 +-------------- 4 files changed, 217 insertions(+), 173 deletions(-) create mode 100644 Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index fb32c0785..214f8c327 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 9e255e1be..17a251ebf 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -370,6 +370,10 @@ 709A14A6283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; 709A14A7283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; 709A14A8283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; + 709A14AB2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; + 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; + 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; + 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -1003,6 +1007,7 @@ 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; + 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseClassLevelPermisioinable.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1446,6 +1451,7 @@ F97B45C524D9C6F200F4A88B /* Fetchable.swift */, 705A9A2E25991C1400B3547F /* Fileable.swift */, 70BC988F252A5B5C00FF3074 /* Objectable.swift */, + 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */, 709A14902839A60600BF85E5 /* ParseIndex.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, @@ -2350,6 +2356,7 @@ F97B463324D9C74400F4A88B /* URLSession.swift in Sources */, F97B464E24D9C78B00F4A88B /* Add.swift in Sources */, 703B095326BF47FD005A112F /* ParseTwitter+async.swift in Sources */, + 709A14AB2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9890252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FE24D9C6F200F4A88B /* ParseFile.swift in Sources */, F97B45EE24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, @@ -2591,6 +2598,7 @@ F97B463424D9C74400F4A88B /* URLSession.swift in Sources */, F97B464F24D9C78B00F4A88B /* Add.swift in Sources */, 703B095426BF47FD005A112F /* ParseTwitter+async.swift in Sources */, + 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9891252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FF24D9C6F200F4A88B /* ParseFile.swift in Sources */, F97B45EF24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, @@ -2935,6 +2943,7 @@ F97B464924D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */, 703B095626BF47FD005A112F /* ParseTwitter+async.swift in Sources */, + 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9893252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465524D9C78C00F4A88B /* AddUnique.swift in Sources */, F97B464D24D9C78B00F4A88B /* Delete.swift in Sources */, @@ -3082,6 +3091,7 @@ F97B464824D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */, 703B095526BF47FD005A112F /* ParseTwitter+async.swift in Sources */, + 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9892252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465424D9C78C00F4A88B /* AddUnique.swift in Sources */, F97B464C24D9C78B00F4A88B /* Delete.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift new file mode 100644 index 000000000..7d81a2fb3 --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift @@ -0,0 +1,204 @@ +// +// ParseClassLevelPermisioinable.swift +// ParseSwift +// +// Created by Corey Baker on 5/27/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +public protocol ParseClassLevelPermisioinable: Codable, Equatable { + var protectedFields: [String: [String]]? { get set } + var readUserFields: [String]? { get set } + var writeUserFields: [String]? { get set } + + /** + Get the protected fields for the given `ParseUser` objectId. + + - parameter user: The `ParseUser` objectId access to check. + - returns: The protected fields. + */ + func getProtectedFields(_ objectId: String) -> [String] + + /** + Get the protected fields for the given `ParseUser`. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + - throws: An error of type `ParseError`. + */ + func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser + + /** + Get the protected fields for the given `ParseUser` pointer. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + */ + func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser + + /** + Get the protected fields for the given `ParseRole`. + + - parameter role: The `ParseRole` access to check. + - returns: The protected fields. + - throws: An error of type `ParseError`. + */ + func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole + + /** + Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: [String], for objectId: String) -> Self + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser + + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole +} + +// MARK: Protected +public extension ParseClassLevelPermisioinable { + internal func getProtected(_ keyPath: KeyPath, + for entity: String) -> [String] { + self[keyPath: keyPath]?[entity] ?? [] + } + + internal func setProtected(_ keyPath: WritableKeyPath, + fields: [String], + for entity: String) -> Self { + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = fields + } else { + mutableCLP[keyPath: keyPath] = [entity: fields] + } + return mutableCLP + } + + func getProtectedFields(_ objectId: String) -> [String] { + getProtected(\.protectedFields, for: objectId) + } + + func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser { + let objectId = try user.toPointer().objectId + return getProtectedFields(objectId) + } + + func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser { + getProtectedFields(user.objectId) + } + + func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return getProtectedFields(roleNameAccess) + } + + func setProtectedFields(_ fields: [String], for objectId: String) -> Self { + setProtected(\.protectedFields, fields: fields, for: objectId) + } + + func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setProtectedFields(fields, for: objectId) + } + + func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { + setProtectedFields(fields, for: user.objectId) + } + + func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setProtectedFields(fields, for: roleNameAccess) + } +} + +// MARK: UserFields +extension ParseClassLevelPermisioinable { + func getUser(_ keyPath: KeyPath) -> [String] { + self[keyPath: keyPath] ?? [] + } + + func setUser(_ keyPath: WritableKeyPath, + fields: [String]) -> Self { + var mutableCLP = self + mutableCLP[keyPath: keyPath] = fields + return mutableCLP + } +} + +// MARK: WriteUserFields +public extension ParseClassLevelPermisioinable { + + /** + Get the `writeUserFields`. + + - returns: User pointer fields. + */ + func getWriteUserFields() -> [String] { + getUser(\.writeUserFields) + } + + /** + Sets permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteUser(_ fields: [String]) -> Self { + setUser(\.writeUserFields, fields: fields) + } +} + +// MARK: ReadUserFields +public extension ParseClassLevelPermisioinable { + + /** + Get the `readUserFields`. + + - returns: User pointer fields. + */ + func getReadUserFields() -> [String] { + getUser(\.readUserFields) + } + + /** + Sets permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadUser(_ fields: [String]) -> Self { + setUser(\.readUserFields, fields: fields) + } +} diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index ded6b29df..510c6bade 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -9,7 +9,7 @@ import Foundation /// Class Level Permissions for `ParseSchema`. -public struct ParseCLP: Codable, Equatable { +public struct ParseCLP: ParseClassLevelPermisioinable { public var get: [String: Bool]? public var find: [String: Bool]? @@ -48,22 +48,6 @@ public struct ParseCLP: Codable, Equatable { return mutableCLP } - func getProtected(_ keyPath: KeyPath, - for entity: String) -> [String] { - self[keyPath: keyPath]?[entity] ?? [] - } - - func setProtected(_ keyPath: WritableKeyPath, - fields: [String], - for entity: String) -> Self { - var mutableCLP = self - if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[entity] = fields - } else { - mutableCLP[keyPath: keyPath] = [entity: fields] - } - return mutableCLP - } } // MARK: Default Implementation @@ -169,160 +153,6 @@ public extension ParseCLP { let roleNameAccess = try ParseACL.getRoleAccessName(role) return setAccess(keyPath, to: allow, for: roleNameAccess) } - - func getUser(_ keyPath: KeyPath) -> [String] { - self[keyPath: keyPath] ?? [] - } - - func setUser(_ keyPath: WritableKeyPath, - fields: [String]) -> Self { - var mutableCLP = self - mutableCLP[keyPath: keyPath] = fields - return mutableCLP - } -} - -// MARK: Protected -public extension ParseCLP { - - /** - Get the protected fields for the given `ParseUser` objectId. - - - parameter user: The `ParseUser` objectId access to check. - - returns: The protected fields. - */ - func getProtectedFields(_ objectId: String) -> [String] { - getProtected(\.protectedFields, for: objectId) - } - - /** - Get the protected fields for the given `ParseUser`. - - - parameter user: The `ParseUser` access to check. - - returns: The protected fields. - - throws: An error of type `ParseError`. - */ - func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser { - let objectId = try user.toPointer().objectId - return getProtectedFields(objectId) - } - - /** - Get the protected fields for the given `ParseUser` pointer. - - - parameter user: The `ParseUser` access to check. - - returns: The protected fields. - */ - func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser { - getProtectedFields(user.objectId) - } - - /** - Get the protected fields for the given `ParseRole`. - - - parameter role: The `ParseRole` access to check. - - returns: The protected fields. - - throws: An error of type `ParseError`. - */ - func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return getProtectedFields(roleNameAccess) - } - - /** - Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` objectId to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: [String], for objectId: String) -> Self { - setProtected(\.protectedFields, fields: fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return setProtectedFields(fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { - setProtectedFields(fields, for: user.objectId) - } - - /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. - - - parameter for: The `ParseRole` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setProtectedFields(fields, for: roleNameAccess) - } -} - -// MARK: WriteUserFields -public extension ParseCLP { - - /** - Get the `writeUserFields`. - - - returns: User pointer fields. - */ - func getWriteUserFields() -> [String] { - getUser(\.writeUserFields) - } - - /** - Sets permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setWriteUser(_ fields: [String]) -> Self { - setUser(\.writeUserFields, fields: fields) - } -} - -// MARK: ReadUserFields -public extension ParseCLP { - - /** - Get the `readUserFields`. - - - returns: User pointer fields. - */ - func getReadUserFields() -> [String] { - getUser(\.readUserFields) - } - - /** - Sets permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setReadUser(_ fields: [String]) -> Self { - setUser(\.readUserFields, fields: fields) - } } // MARK: WriteAccess From 874eced8c16071c1a5d64e563d59c7b56a43ac39 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Fri, 27 May 2022 15:14:28 -0400 Subject: [PATCH 20/55] Using CLP protocol --- .../Contents.swift | 13 +- ParseSwift.xcodeproj/project.pbxproj | 10 + .../ParseClassLevelPermisioinable.swift | 270 ++++++++++++++---- Sources/ParseSwift/Types/ParseCLP.swift | 7 +- .../ParseSwift/Types/ParseCLPPointer.swift | 56 ++++ Sources/ParseSwift/Types/ParseSchema.swift | 6 +- 6 files changed, 297 insertions(+), 65 deletions(-) create mode 100644 Sources/ParseSwift/Types/ParseCLPPointer.swift diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 0a1b1c77d..df716de85 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -45,13 +45,12 @@ extension GameScore2 { } //: First lets create a new CLP for the new schema. -let clp = ParseCLP(requireAuthentication: false, publicAccess: true) - .setAccessPublic(\.count, to: false) - .setAccessRequiresAuthentication(\.count, to: true) - .setAccessRequiresAuthentication(\.addField, to: true) +let clp = ParseCLP(requireAuthentication: true, publicAccess: false) + .setAccessPublic(\.get, to: true) + .setAccessPublic(\.find, to: true) //: Next we use the CLP to create the new schema and add fields to it. -var gameScoreSchema = ParseSchema(classLevelPermissions: clp) +var gameScoreSchema = ParseSchema(classLevelPermissions: clp) .addField("points", type: .number, options: ParseFieldOptions(required: false, defauleValue: nil)) @@ -60,7 +59,7 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: clp) options: ParseFieldOptions(required: false, defauleValue: nil)) //: Now lets create the schema on the server. -gameScoreSchema.create { result in +gameScoreSchema.fetch { result in switch result { case .success(let savedSchema): print("Check GameScore2 in Dashboard. \(savedSchema)") @@ -69,4 +68,6 @@ gameScoreSchema.create { result in } } +let clpPointer = ParseCLPPointer() + //: [Next](@next) diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 17a251ebf..ea389dcdd 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -374,6 +374,10 @@ 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; + 709A14B0284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; + 709A14B1284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; + 709A14B2284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; + 709A14B3284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -1008,6 +1012,7 @@ 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseClassLevelPermisioinable.swift; sourceTree = ""; }; + 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLPPointer.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1711,6 +1716,7 @@ 70170A482656E2FE0070C905 /* ParseAnalytics+combine.swift */, 91285B122698DBF20051B544 /* ParseBytes.swift */, 709A149F2839CABD00BF85E5 /* ParseCLP.swift */, + 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */, 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */, 703B090626BD9764005A112F /* ParseCloud+async.swift */, 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */, @@ -2258,6 +2264,7 @@ 918CED592684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0625D9718C0048EC1B /* Data.swift in Sources */, F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */, + 709A14B0284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C12762F313004C9757 /* QueryWhere.swift in Sources */, 70170A442656B02D0070C905 /* ParseAnalytics.swift in Sources */, 70110D52250680140091CC1D /* ParseConstants.swift in Sources */, @@ -2500,6 +2507,7 @@ 918CED5A2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0725D9718C0048EC1B /* Data.swift in Sources */, F97B466024D9C7B500F4A88B /* KeychainStore.swift in Sources */, + 709A14B1284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C22762F313004C9757 /* QueryWhere.swift in Sources */, 70170A452656B02D0070C905 /* ParseAnalytics.swift in Sources */, 70110D53250680140091CC1D /* ParseConstants.swift in Sources */, @@ -2845,6 +2853,7 @@ 703B090A26BD9764005A112F /* ParseCloud+async.swift in Sources */, 918CED5C2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0925D9718C0048EC1B /* Data.swift in Sources */, + 709A14B3284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C42762F313004C9757 /* QueryWhere.swift in Sources */, F97B460524D9C6F200F4A88B /* NoBody.swift in Sources */, 70170A472656B02D0070C905 /* ParseAnalytics.swift in Sources */, @@ -2993,6 +3002,7 @@ 703B090926BD9764005A112F /* ParseCloud+async.swift in Sources */, 918CED5B2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0825D9718C0048EC1B /* Data.swift in Sources */, + 709A14B2284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C32762F313004C9757 /* QueryWhere.swift in Sources */, F97B460424D9C6F200F4A88B /* NoBody.swift in Sources */, 70170A462656B02D0070C905 /* ParseAnalytics.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift index 7d81a2fb3..b6ee9b1e8 100644 --- a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift +++ b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift @@ -9,9 +9,51 @@ import Foundation public protocol ParseClassLevelPermisioinable: Codable, Equatable { - var protectedFields: [String: [String]]? { get set } - var readUserFields: [String]? { get set } - var writeUserFields: [String]? { get set } + var protectedFields: [String: Set]? { get set } + var readUserFields: Set? { get set } + var writeUserFields: Set? { get set } +} + +// MARK: Protected +public extension ParseClassLevelPermisioinable { + internal func getProtected(_ keyPath: KeyPath]?>, + for entity: String) -> Set { + self[keyPath: keyPath]?[entity] ?? [] + } + + internal func setProtected(_ keyPath: WritableKeyPath]?>, + fields: Set, + for entity: String) -> Self { + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = fields + } else { + mutableCLP[keyPath: keyPath] = [entity: fields] + } + return mutableCLP + } + + internal func addProtected(_ keyPath: WritableKeyPath]?>, + fields: Set, + for entity: String) -> Self { + if let currentSet = self[keyPath: keyPath]?[entity] { + var mutableCLP = self + mutableCLP[keyPath: keyPath]?[entity] = currentSet.union(fields) + return mutableCLP + } else { + return setProtected(keyPath, fields: fields, for: entity) + } + } + + internal func removeProtected(_ keyPath: WritableKeyPath]?>, + fields: Set, + for entity: String) -> Self { + var mutableCLP = self + fields.forEach { + mutableCLP[keyPath: keyPath]?[entity]?.remove($0) + } + return mutableCLP + } /** Get the protected fields for the given `ParseUser` objectId. @@ -19,7 +61,9 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - parameter user: The `ParseUser` objectId access to check. - returns: The protected fields. */ - func getProtectedFields(_ objectId: String) -> [String] + func getProtectedFields(_ objectId: String) -> Set { + getProtected(\.protectedFields, for: objectId) + } /** Get the protected fields for the given `ParseUser`. @@ -28,7 +72,10 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - returns: The protected fields. - throws: An error of type `ParseError`. */ - func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser + func getProtectedFields(_ user: U) throws -> Set where U: ParseUser { + let objectId = try user.toPointer().objectId + return getProtectedFields(objectId) + } /** Get the protected fields for the given `ParseUser` pointer. @@ -36,7 +83,9 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - parameter user: The `ParseUser` access to check. - returns: The protected fields. */ - func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser + func getProtectedFields(_ user: Pointer) -> Set where U: ParseUser { + getProtectedFields(user.objectId) + } /** Get the protected fields for the given `ParseRole`. @@ -45,7 +94,10 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - returns: The protected fields. - throws: An error of type `ParseError`. */ - func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole + func getProtectedFields(_ role: R) throws -> Set where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return getProtectedFields(roleNameAccess) + } /** Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. @@ -55,7 +107,9 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setProtectedFields(_ fields: [String], for objectId: String) -> Self + func setProtectedFields(_ fields: Set, for objectId: String) -> Self { + setProtected(\.protectedFields, fields: fields, for: objectId) + } /** Set whether the given `ParseUser` is allowed to retrieve fields from this class. @@ -65,7 +119,10 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser + func setProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setProtectedFields(fields, for: objectId) + } /** Set whether the given `ParseUser` is allowed to retrieve fields from this class. @@ -74,7 +131,9 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser + func setProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + setProtectedFields(fields, for: user.objectId) + } /** Set whether the given `ParseRole` is allowed to retrieve fields from this class. @@ -84,77 +143,142 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole -} - -// MARK: Protected -public extension ParseClassLevelPermisioinable { - internal func getProtected(_ keyPath: KeyPath, - for entity: String) -> [String] { - self[keyPath: keyPath]?[entity] ?? [] - } - - internal func setProtected(_ keyPath: WritableKeyPath, - fields: [String], - for entity: String) -> Self { - var mutableCLP = self - if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[entity] = fields - } else { - mutableCLP[keyPath: keyPath] = [entity: fields] - } - return mutableCLP + func setProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setProtectedFields(fields, for: roleNameAccess) } - func getProtectedFields(_ objectId: String) -> [String] { - getProtected(\.protectedFields, for: objectId) + /** + Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for objectId: String) -> Self { + addProtected(\.protectedFields, fields: fields, for: objectId) } - func getProtectedFields(_ user: U) throws -> [String] where U: ParseUser { + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return getProtectedFields(objectId) + return addProtectedFields(fields, for: objectId) } - func getProtectedFields(_ user: Pointer) -> [String] where U: ParseUser { - getProtectedFields(user.objectId) + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + addProtectedFields(fields, for: user.objectId) } - func getProtectedFields(_ role: R) throws -> [String] where R: ParseRole { + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return getProtectedFields(roleNameAccess) + return addProtectedFields(fields, for: roleNameAccess) } - func setProtectedFields(_ fields: [String], for objectId: String) -> Self { - setProtected(\.protectedFields, fields: fields, for: objectId) + /** + Remove the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for objectId: String) -> Self { + removeProtected(\.protectedFields, fields: fields, for: objectId) } - func setProtectedFields(_ fields: [String], for user: U) throws -> Self where U: ParseUser { + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setProtectedFields(fields, for: objectId) + return removeProtectedFields(fields, for: objectId) } - func setProtectedFields(_ fields: [String], for user: Pointer) -> Self where U: ParseUser { - setProtectedFields(fields, for: user.objectId) + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + removeProtectedFields(fields, for: user.objectId) } - func setProtectedFields(_ fields: [String], for role: R) throws -> Self where R: ParseRole { + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setProtectedFields(fields, for: roleNameAccess) + return removeProtectedFields(fields, for: roleNameAccess) } } // MARK: UserFields extension ParseClassLevelPermisioinable { - func getUser(_ keyPath: KeyPath) -> [String] { + func getUser(_ keyPath: KeyPath?>) -> Set { self[keyPath: keyPath] ?? [] } - func setUser(_ keyPath: WritableKeyPath, - fields: [String]) -> Self { + func setUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { var mutableCLP = self mutableCLP[keyPath: keyPath] = fields return mutableCLP } + + func addUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { + if let currentSet = self[keyPath: keyPath] { + var mutableCLP = self + mutableCLP[keyPath: keyPath] = currentSet.union(currentSet) + return mutableCLP + } else { + return setUser(keyPath, fields: fields) + } + } + + func removeUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { + var mutableCLP = self + fields.forEach { + mutableCLP[keyPath: keyPath]?.remove($0) + } + return mutableCLP + } } // MARK: WriteUserFields @@ -165,7 +289,7 @@ public extension ParseClassLevelPermisioinable { - returns: User pointer fields. */ - func getWriteUserFields() -> [String] { + func getWriteUserFields() -> Set { getUser(\.writeUserFields) } @@ -175,9 +299,29 @@ public extension ParseClassLevelPermisioinable { - parameter fields: User pointer fields. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setWriteUser(_ fields: [String]) -> Self { + func setWriteUser(_ fields: Set) -> Self { setUser(\.writeUserFields, fields: fields) } + + /** + Adds permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addWriteUser(_ fields: Set) -> Self { + addUser(\.writeUserFields, fields: fields) + } + + /** + Adds permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeWriteUser(_ fields: Set) -> Self { + removeUser(\.writeUserFields, fields: fields) + } } // MARK: ReadUserFields @@ -188,7 +332,7 @@ public extension ParseClassLevelPermisioinable { - returns: User pointer fields. */ - func getReadUserFields() -> [String] { + func getReadUserFields() -> Set { getUser(\.readUserFields) } @@ -198,7 +342,27 @@ public extension ParseClassLevelPermisioinable { - parameter fields: User pointer fields. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setReadUser(_ fields: [String]) -> Self { + func setReadUser(_ fields: Set) -> Self { setUser(\.readUserFields, fields: fields) } + + /** + Adds permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addReadUser(_ fields: Set) -> Self { + addUser(\.readUserFields, fields: fields) + } + + /** + Removes permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeReadUser(_ fields: Set) -> Self { + removeUser(\.readUserFields, fields: fields) + } } diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 510c6bade..617d18300 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -18,13 +18,14 @@ public struct ParseCLP: ParseClassLevelPermisioinable { public var update: [String: Bool]? public var delete: [String: Bool]? public var addField: [String: Bool]? - public var protectedFields: [String: [String]]? - public var readUserFields: [String]? - public var writeUserFields: [String]? + public var protectedFields: [String: Set]? + public var readUserFields: Set? + public var writeUserFields: Set? enum Access: String { case requiresAuthentication case publicScope = "*" + case pointerFields } /// Creates an empty CLP type. diff --git a/Sources/ParseSwift/Types/ParseCLPPointer.swift b/Sources/ParseSwift/Types/ParseCLPPointer.swift new file mode 100644 index 000000000..7f29ca7fa --- /dev/null +++ b/Sources/ParseSwift/Types/ParseCLPPointer.swift @@ -0,0 +1,56 @@ +// +// ParseCLPPointer.swift +// ParseSwift +// +// Created by Corey Baker on 5/27/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +/// Class Level Permissions for `ParseSchema`. +public struct ParseCLPPointer: ParseClassLevelPermisioinable { + + public var get: [String: Set]? + public var find: [String: Set]? + public var count: [String: Set]? + public var create: [String: Set]? + public var update: [String: Set]? + public var delete: [String: Set]? + public var addField: [String: Set]? + public var protectedFields: [String: Set]? + public var readUserFields: Set? + public var writeUserFields: Set? + + /// Creates an empty CLP type. + public init() { } +} + +public extension ParseCLPPointer { + + func getPointer(_ keyPath: KeyPath]?>) -> Set { + self[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] ?? [] + } + + func setPointer(_ keyPath: WritableKeyPath]?>, + fields: Set) -> Self { + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] = fields + } else { + mutableCLP[keyPath: keyPath] = [ParseCLP.Access.pointerFields.rawValue: fields] + } + return mutableCLP + } + + func addPointer(_ keyPath: WritableKeyPath]?>, + fields: Set) -> Self { + var mutableCLP = self + if let currentSet = mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] { + mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] = currentSet.union(fields) + } else { + mutableCLP[keyPath: keyPath] = [ParseCLP.Access.pointerFields.rawValue: fields] + } + return mutableCLP + } +} diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index bbc777509..0f94f26b4 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -14,7 +14,7 @@ import Foundation use the master key in server-side applications where the key is kept secure and not exposed to the public. */ -public struct ParseSchema: ParseType, Decodable { +public struct ParseSchema: ParseType, Decodable { /// The class name of the `ParseSchema`. public var className: String @@ -26,7 +26,7 @@ public struct ParseSchema: ParseType, Decodable { internal var indexes: [String: AnyCodable]? /// The CLPs of this `ParseSchema`. - public var classLevelPermissions: ParseCLP? + public var classLevelPermissions: SchemaCLP? /** Get the current fields for this `ParseSchema`. @@ -63,7 +63,7 @@ public extension ParseSchema { self.init(className: SchemaObject.className) } - init(classLevelPermissions: ParseCLP) { + init(classLevelPermissions: SchemaCLP) { self.init(className: SchemaObject.className) self.classLevelPermissions = classLevelPermissions } From ae36cad10c163d1c23a34f7eb4e913091513994d Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Fri, 27 May 2022 19:48:35 -0400 Subject: [PATCH 21/55] improve CLP signatures --- .../Contents.swift | 10 +- ParseSwift.playground/Sources/Common.swift | 4 +- ParseSwift.xcodeproj/project.pbxproj | 10 - .../ParseClassLevelPermisioinable.swift | 353 ---------- Sources/ParseSwift/Types/ParseCLP.swift | 636 ++++++++++++++++-- .../ParseSwift/Types/ParseCLPPointer.swift | 56 -- Sources/ParseSwift/Types/ParseSchema.swift | 6 +- 7 files changed, 600 insertions(+), 475 deletions(-) delete mode 100644 Sources/ParseSwift/Types/ParseCLPPointer.swift diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index df716de85..0a94e29cd 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -46,11 +46,11 @@ extension GameScore2 { //: First lets create a new CLP for the new schema. let clp = ParseCLP(requireAuthentication: true, publicAccess: false) - .setAccessPublic(\.get, to: true) - .setAccessPublic(\.find, to: true) + .setAccessPublic(true, on: .get) + .setAccessPublic(true, on: .find) //: Next we use the CLP to create the new schema and add fields to it. -var gameScoreSchema = ParseSchema(classLevelPermissions: clp) +var gameScoreSchema = ParseSchema(classLevelPermissions: clp) .addField("points", type: .number, options: ParseFieldOptions(required: false, defauleValue: nil)) @@ -59,7 +59,7 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: c options: ParseFieldOptions(required: false, defauleValue: nil)) //: Now lets create the schema on the server. -gameScoreSchema.fetch { result in +gameScoreSchema.create { result in switch result { case .success(let savedSchema): print("Check GameScore2 in Dashboard. \(savedSchema)") @@ -68,6 +68,4 @@ gameScoreSchema.fetch { result in } } -let clpPointer = ParseCLPPointer() - //: [Next](@next) diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index 214f8c327..fb32c0785 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index ea389dcdd..17a251ebf 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -374,10 +374,6 @@ 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; - 709A14B0284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; - 709A14B1284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; - 709A14B2284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; - 709A14B3284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -1012,7 +1008,6 @@ 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseClassLevelPermisioinable.swift; sourceTree = ""; }; - 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLPPointer.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1716,7 +1711,6 @@ 70170A482656E2FE0070C905 /* ParseAnalytics+combine.swift */, 91285B122698DBF20051B544 /* ParseBytes.swift */, 709A149F2839CABD00BF85E5 /* ParseCLP.swift */, - 709A14AF284106FB00BF85E5 /* ParseCLPPointer.swift */, 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */, 703B090626BD9764005A112F /* ParseCloud+async.swift */, 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */, @@ -2264,7 +2258,6 @@ 918CED592684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0625D9718C0048EC1B /* Data.swift in Sources */, F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */, - 709A14B0284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C12762F313004C9757 /* QueryWhere.swift in Sources */, 70170A442656B02D0070C905 /* ParseAnalytics.swift in Sources */, 70110D52250680140091CC1D /* ParseConstants.swift in Sources */, @@ -2507,7 +2500,6 @@ 918CED5A2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0725D9718C0048EC1B /* Data.swift in Sources */, F97B466024D9C7B500F4A88B /* KeychainStore.swift in Sources */, - 709A14B1284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C22762F313004C9757 /* QueryWhere.swift in Sources */, 70170A452656B02D0070C905 /* ParseAnalytics.swift in Sources */, 70110D53250680140091CC1D /* ParseConstants.swift in Sources */, @@ -2853,7 +2845,6 @@ 703B090A26BD9764005A112F /* ParseCloud+async.swift in Sources */, 918CED5C2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0925D9718C0048EC1B /* Data.swift in Sources */, - 709A14B3284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C42762F313004C9757 /* QueryWhere.swift in Sources */, F97B460524D9C6F200F4A88B /* NoBody.swift in Sources */, 70170A472656B02D0070C905 /* ParseAnalytics.swift in Sources */, @@ -3002,7 +2993,6 @@ 703B090926BD9764005A112F /* ParseCloud+async.swift in Sources */, 918CED5B2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0825D9718C0048EC1B /* Data.swift in Sources */, - 709A14B2284106FB00BF85E5 /* ParseCLPPointer.swift in Sources */, 70B4E0C32762F313004C9757 /* QueryWhere.swift in Sources */, F97B460424D9C6F200F4A88B /* NoBody.swift in Sources */, 70170A462656B02D0070C905 /* ParseAnalytics.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift index b6ee9b1e8..23271819e 100644 --- a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift +++ b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift @@ -13,356 +13,3 @@ public protocol ParseClassLevelPermisioinable: Codable, Equatable { var readUserFields: Set? { get set } var writeUserFields: Set? { get set } } - -// MARK: Protected -public extension ParseClassLevelPermisioinable { - internal func getProtected(_ keyPath: KeyPath]?>, - for entity: String) -> Set { - self[keyPath: keyPath]?[entity] ?? [] - } - - internal func setProtected(_ keyPath: WritableKeyPath]?>, - fields: Set, - for entity: String) -> Self { - var mutableCLP = self - if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[entity] = fields - } else { - mutableCLP[keyPath: keyPath] = [entity: fields] - } - return mutableCLP - } - - internal func addProtected(_ keyPath: WritableKeyPath]?>, - fields: Set, - for entity: String) -> Self { - if let currentSet = self[keyPath: keyPath]?[entity] { - var mutableCLP = self - mutableCLP[keyPath: keyPath]?[entity] = currentSet.union(fields) - return mutableCLP - } else { - return setProtected(keyPath, fields: fields, for: entity) - } - } - - internal func removeProtected(_ keyPath: WritableKeyPath]?>, - fields: Set, - for entity: String) -> Self { - var mutableCLP = self - fields.forEach { - mutableCLP[keyPath: keyPath]?[entity]?.remove($0) - } - return mutableCLP - } - - /** - Get the protected fields for the given `ParseUser` objectId. - - - parameter user: The `ParseUser` objectId access to check. - - returns: The protected fields. - */ - func getProtectedFields(_ objectId: String) -> Set { - getProtected(\.protectedFields, for: objectId) - } - - /** - Get the protected fields for the given `ParseUser`. - - - parameter user: The `ParseUser` access to check. - - returns: The protected fields. - - throws: An error of type `ParseError`. - */ - func getProtectedFields(_ user: U) throws -> Set where U: ParseUser { - let objectId = try user.toPointer().objectId - return getProtectedFields(objectId) - } - - /** - Get the protected fields for the given `ParseUser` pointer. - - - parameter user: The `ParseUser` access to check. - - returns: The protected fields. - */ - func getProtectedFields(_ user: Pointer) -> Set where U: ParseUser { - getProtectedFields(user.objectId) - } - - /** - Get the protected fields for the given `ParseRole`. - - - parameter role: The `ParseRole` access to check. - - returns: The protected fields. - - throws: An error of type `ParseError`. - */ - func getProtectedFields(_ role: R) throws -> Set where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return getProtectedFields(roleNameAccess) - } - - /** - Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` objectId to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: Set, for objectId: String) -> Self { - setProtected(\.protectedFields, fields: fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return setProtectedFields(fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { - setProtectedFields(fields, for: user.objectId) - } - - /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. - - - parameter for: The `ParseRole` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func setProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setProtectedFields(fields, for: roleNameAccess) - } - - /** - Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` objectId to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func addProtectedFields(_ fields: Set, for objectId: String) -> Self { - addProtected(\.protectedFields, fields: fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func addProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return addProtectedFields(fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func addProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { - addProtectedFields(fields, for: user.objectId) - } - - /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. - - - parameter for: The `ParseRole` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func addProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return addProtectedFields(fields, for: roleNameAccess) - } - - /** - Remove the given `ParseUser` objectId is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` objectId to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func removeProtectedFields(_ fields: Set, for objectId: String) -> Self { - removeProtected(\.protectedFields, fields: fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func removeProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return removeProtectedFields(fields, for: objectId) - } - - /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func removeProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { - removeProtectedFields(fields, for: user.objectId) - } - - /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. - - - parameter for: The `ParseRole` to provide/restrict access to. - - parameter fields: The fields to be protected. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - throws: An error of type `ParseError`. - */ - func removeProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return removeProtectedFields(fields, for: roleNameAccess) - } -} - -// MARK: UserFields -extension ParseClassLevelPermisioinable { - func getUser(_ keyPath: KeyPath?>) -> Set { - self[keyPath: keyPath] ?? [] - } - - func setUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - var mutableCLP = self - mutableCLP[keyPath: keyPath] = fields - return mutableCLP - } - - func addUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - if let currentSet = self[keyPath: keyPath] { - var mutableCLP = self - mutableCLP[keyPath: keyPath] = currentSet.union(currentSet) - return mutableCLP - } else { - return setUser(keyPath, fields: fields) - } - } - - func removeUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - var mutableCLP = self - fields.forEach { - mutableCLP[keyPath: keyPath]?.remove($0) - } - return mutableCLP - } -} - -// MARK: WriteUserFields -public extension ParseClassLevelPermisioinable { - - /** - Get the `writeUserFields`. - - - returns: User pointer fields. - */ - func getWriteUserFields() -> Set { - getUser(\.writeUserFields) - } - - /** - Sets permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setWriteUser(_ fields: Set) -> Self { - setUser(\.writeUserFields, fields: fields) - } - - /** - Adds permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func addWriteUser(_ fields: Set) -> Self { - addUser(\.writeUserFields, fields: fields) - } - - /** - Adds permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func removeWriteUser(_ fields: Set) -> Self { - removeUser(\.writeUserFields, fields: fields) - } -} - -// MARK: ReadUserFields -public extension ParseClassLevelPermisioinable { - - /** - Get the `readUserFields`. - - - returns: User pointer fields. - */ - func getReadUserFields() -> Set { - getUser(\.readUserFields) - } - - /** - Sets permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setReadUser(_ fields: Set) -> Self { - setUser(\.readUserFields, fields: fields) - } - - /** - Adds permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func addReadUser(_ fields: Set) -> Self { - addUser(\.readUserFields, fields: fields) - } - - /** - Removes permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func removeReadUser(_ fields: Set) -> Self { - removeUser(\.readUserFields, fields: fields) - } -} diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 617d18300..45792e1e1 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -9,19 +9,65 @@ import Foundation /// Class Level Permissions for `ParseSchema`. -public struct ParseCLP: ParseClassLevelPermisioinable { - - public var get: [String: Bool]? - public var find: [String: Bool]? - public var count: [String: Bool]? - public var create: [String: Bool]? - public var update: [String: Bool]? - public var delete: [String: Bool]? - public var addField: [String: Bool]? +public struct ParseCLP: Codable, Equatable { + + var get: [String: AnyCodable]? + var find: [String: AnyCodable]? + var count: [String: AnyCodable]? + var create: [String: AnyCodable]? + var update: [String: AnyCodable]? + var delete: [String: AnyCodable]? + var addField: [String: AnyCodable]? public var protectedFields: [String: Set]? public var readUserFields: Set? public var writeUserFields: Set? + public enum Action { + case get, find, count, create, update, delete, addField + + internal func keyPath() -> KeyPath { + let keyPath: KeyPath + switch self { + case .get: + keyPath = \.get + case .find: + keyPath = \.find + case .count: + keyPath = \.count + case .create: + keyPath = \.create + case .update: + keyPath = \.update + case .delete: + keyPath = \.delete + case .addField: + keyPath = \.addField + } + return keyPath + } + + internal func writableKeyPath() -> WritableKeyPath { + let keyPath: WritableKeyPath + switch self { + case .get: + keyPath = \.get + case .find: + keyPath = \.find + case .count: + keyPath = \.count + case .create: + keyPath = \.create + case .update: + keyPath = \.update + case .delete: + keyPath = \.delete + case .addField: + keyPath = \.addField + } + return keyPath + } + } + enum Access: String { case requiresAuthentication case publicScope = "*" @@ -31,24 +77,72 @@ public struct ParseCLP: ParseClassLevelPermisioinable { /// Creates an empty CLP type. public init() { } - func hasAccess(_ keyPath: KeyPath, + func getPointerFields(_ keyPath: KeyPath) -> Set { + self[keyPath: keyPath]?[Access.pointerFields.rawValue]?.value as? Set ?? [] + } + + func hasAccess(_ keyPath: KeyPath, for entity: String) -> Bool { - self[keyPath: keyPath]?[entity] ?? false + self[keyPath: keyPath]?[entity]?.value as? Bool ?? false } - func setAccess(_ keyPath: WritableKeyPath, - to allow: Bool, + func setAccess(_ allow: Bool, + on keyPath: WritableKeyPath, for entity: String) -> Self { let allowed: Bool? = allow ? allow : nil var mutableCLP = self + if let allowed = allowed { + let value = AnyCodable(allowed) + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = value + } else { + mutableCLP[keyPath: keyPath] = [entity: value] + } + } else { + mutableCLP[keyPath: keyPath]?[entity] = nil + } + return mutableCLP + } + + func setPointer(_ fields: Set, + on keyPath: WritableKeyPath, + for entity: String) -> Self { + var mutableCLP = self + let value = AnyCodable(fields) if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[entity] = allowed - } else if let allowed = allowed { - mutableCLP[keyPath: keyPath] = [entity: allowed] + mutableCLP[keyPath: keyPath]?[entity] = value + } else { + mutableCLP[keyPath: keyPath] = [entity: value] } return mutableCLP } + func addPointer(_ fields: Set, + on keyPath: WritableKeyPath, + for entity: String) -> Self { + + if let currentSet = self[keyPath: keyPath]?[entity]?.value as? Set { + var mutableCLP = self + mutableCLP[keyPath: keyPath]?[entity] = AnyCodable(currentSet.union(fields)) + return mutableCLP + } else { + return setPointer(fields, on: keyPath, for: entity) + } + } + + func removePointer(_ fields: Set, + on keyPath: WritableKeyPath, + for entity: String) -> Self { + + if var currentSet = self[keyPath: keyPath]?[entity]?.value as? Set { + var mutableCLP = self + fields.forEach { currentSet.remove($0) } + mutableCLP[keyPath: keyPath]?[entity] = AnyCodable(currentSet) + return mutableCLP + } else { + return setPointer(fields, on: keyPath, for: entity) + } + } } // MARK: Default Implementation @@ -90,8 +184,18 @@ public extension ParseCLP { action on a `ParseSchema`: get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. */ - func hasAccessPublic(_ keyPath: KeyPath) throws -> Bool { - hasAccess(keyPath, for: Access.publicScope.rawValue) + func getPointerFields(_ action: Action) throws -> Set { + getPointerFields(action.keyPath()) + } + + /** + Checks if get/find/count/create/update/delete/addField actions currently have public access. + - parameter keyPath: Any of the following keyPaths that represent an + action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - returns: **true** if access is allowed, **false** otherwise. + */ + func hasAccessPublic(_ action: Action) throws -> Bool { + hasAccess(action.keyPath(), for: Access.publicScope.rawValue) } /** @@ -101,58 +205,147 @@ public extension ParseCLP { - returns: **true** if access is allowed, **false** otherwise. - warning: Requires Parse Server 2.3.0+. */ - func hasAccessRequiresAuthentication(_ keyPath: KeyPath) throws -> Bool { - hasAccess(keyPath, for: Access.requiresAuthentication.rawValue) + func hasAccessRequiresAuthentication(_ action: Action) throws -> Bool { + hasAccess(action.keyPath(), for: Access.requiresAuthentication.rawValue) + } + + func hasAccess(_ action: Action, + for objectId: String) throws -> Bool { + return hasAccess(action.keyPath(), for: objectId) } - func hasAccess(_ keyPath: KeyPath, + func hasAccess(_ action: Action, for user: U) throws -> Bool where U: ParseUser { let objectId = try user.toPointer().objectId - return hasAccess(keyPath, for: objectId) + return hasAccess(action.keyPath(), for: objectId) } - func hasAccess(_ keyPath: KeyPath, + func hasAccess(_ action: Action, for user: Pointer) throws -> Bool where U: ParseUser { - hasAccess(keyPath, for: user.objectId) + hasAccess(action.keyPath(), for: user.objectId) } - func hasAccess(_ keyPath: KeyPath, + func hasAccess(_ action: Action, for role: R) throws -> Bool where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return hasAccess(keyPath, for: roleNameAccess) + return hasAccess(action.keyPath(), for: roleNameAccess) } - func setAccessPublic(_ keyPath: WritableKeyPath, - to allow: Bool) -> Self { - setAccess(keyPath, to: allow, for: Access.publicScope.rawValue) + func setAccessPublic(_ allow: Bool, + on action: Action) -> Self { + setAccess(allow, on: action.writableKeyPath(), for: Access.publicScope.rawValue) } /** - warning: Requires Parse Server 2.3.0+. */ - func setAccessRequiresAuthentication(_ keyPath: WritableKeyPath, - to allow: Bool) -> Self { - setAccess(keyPath, to: allow, for: Access.requiresAuthentication.rawValue) + func setAccessRequiresAuthentication(_ allow: Bool, + on action: Action) -> Self { + setAccess(allow, on: action.writableKeyPath(), for: Access.requiresAuthentication.rawValue) } - func setAccess(_ keyPath: WritableKeyPath, + func setAccess(_ action: Action, + to allow: Bool, + for objectId: String) -> Self { + setAccess(allow, on: action.writableKeyPath(), for: objectId) + } + + func setAccess(_ action: Action, to allow: Bool, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setAccess(keyPath, to: allow, for: objectId) + return setAccess(allow, on: action.writableKeyPath(), for: objectId) } - func setAccess(_ keyPath: WritableKeyPath, + func setAccess(_ action: Action, to allow: Bool, for user: Pointer) -> Self where U: ParseUser { - setAccess(keyPath, to: allow, for: user.objectId) + setAccess(allow, on: action.writableKeyPath(), for: user.objectId) } - func setAccess(_ keyPath: WritableKeyPath, + func setAccess(_ action: Action, to allow: Bool, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setAccess(keyPath, to: allow, for: roleNameAccess) + return setAccess(allow, on: action.writableKeyPath(), for: roleNameAccess) + } + + func setPointerFields(_ action: Action, + to fields: Set, + for objectId: String) -> Self { + setPointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func setPointerFields(_ action: Action, + to fields: Set, + for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setPointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func setPointerFields(_ action: Action, + to fields: Set, + for user: Pointer) -> Self where U: ParseUser { + setPointer(fields, on: action.writableKeyPath(), for: user.objectId) + } + + func setPointerFields(_ action: Action, + to fields: Set, + for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setPointer(fields, on: action.writableKeyPath(), for: roleNameAccess) + } + + func addPointerFields(_ fields: Set, + on action: Action, + for objectId: String) -> Self { + addPointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func addPointerFields(_ fields: Set, + on action: Action, + for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return addPointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func addPointerFields(_ fields: Set, + on action: Action, + for user: Pointer) -> Self where U: ParseUser { + addPointer(fields, on: action.writableKeyPath(), for: user.objectId) + } + + func addPointerFields(_ fields: Set, + on action: Action, + for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return addPointer(fields, on: action.writableKeyPath(), for: roleNameAccess) + } + + func removePointerFields(_ fields: Set, + on action: Action, + for objectId: String) -> Self { + removePointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func removePointerFields(_ fields: Set, + on action: Action, + for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return removePointer(fields, on: action.writableKeyPath(), for: objectId) + } + + func removePointerFields(_ fields: Set, + on action: Action, + for user: Pointer) -> Self where U: ParseUser { + removePointer(fields, on: action.writableKeyPath(), for: user.objectId) + } + + func removePointerFields(_ fields: Set, + on action: Action, + for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return removePointer(fields, on: action.writableKeyPath(), for: roleNameAccess) } } @@ -290,11 +483,11 @@ public extension ParseCLP { objectId: String, canAddField addField: Bool = false) -> Self { var updatedCLP = self - .setAccess(\.create, to: allow, for: objectId) - .setAccess(\.update, to: allow, for: objectId) - .setAccess(\.delete, to: allow, for: objectId) + .setAccess(allow, on: \.create, for: objectId) + .setAccess(allow, on: \.update, for: objectId) + .setAccess(allow, on: \.delete, for: objectId) if addField { - updatedCLP = updatedCLP.setAccess(\.addField, to: allow, for: objectId) + updatedCLP = updatedCLP.setAccess(allow, on: \.addField, for: objectId) } return updatedCLP } @@ -451,9 +644,9 @@ public extension ParseCLP { func setReadAccess(_ allow: Bool, objectId: String) -> Self { let updatedCLP = self - .setAccess(\.get, to: allow, for: objectId) - .setAccess(\.find, to: allow, for: objectId) - .setAccess(\.count, to: allow, for: objectId) + .setAccess(allow, on: \.get, for: objectId) + .setAccess(allow, on: \.find, for: objectId) + .setAccess(allow, on: \.count, for: objectId) return updatedCLP } @@ -497,6 +690,359 @@ public extension ParseCLP { } } +// MARK: Protected +public extension ParseClassLevelPermisioinable { + internal func getProtected(_ keyPath: KeyPath]?>, + for entity: String) -> Set { + self[keyPath: keyPath]?[entity] ?? [] + } + + internal func setProtected(_ fields: Set, + on keyPath: WritableKeyPath]?>, + for entity: String) -> Self { + var mutableCLP = self + if mutableCLP[keyPath: keyPath] != nil { + mutableCLP[keyPath: keyPath]?[entity] = fields + } else { + mutableCLP[keyPath: keyPath] = [entity: fields] + } + return mutableCLP + } + + internal func addProtected(_ fields: Set, + on keyPath: WritableKeyPath]?>, + for entity: String) -> Self { + if let currentSet = self[keyPath: keyPath]?[entity] { + var mutableCLP = self + mutableCLP[keyPath: keyPath]?[entity] = currentSet.union(fields) + return mutableCLP + } else { + return setProtected(fields, on: keyPath, for: entity) + } + } + + internal func removeProtected(_ fields: Set, + on keyPath: WritableKeyPath]?>, + for entity: String) -> Self { + var mutableCLP = self + fields.forEach { + mutableCLP[keyPath: keyPath]?[entity]?.remove($0) + } + return mutableCLP + } + + /** + Get the protected fields for the given `ParseUser` objectId. + + - parameter user: The `ParseUser` objectId access to check. + - returns: The protected fields. + */ + func getProtectedFields(_ objectId: String) -> Set { + getProtected(\.protectedFields, for: objectId) + } + + /** + Get the protected fields for the given `ParseUser`. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + - throws: An error of type `ParseError`. + */ + func getProtectedFields(_ user: U) throws -> Set where U: ParseUser { + let objectId = try user.toPointer().objectId + return getProtectedFields(objectId) + } + + /** + Get the protected fields for the given `ParseUser` pointer. + + - parameter user: The `ParseUser` access to check. + - returns: The protected fields. + */ + func getProtectedFields(_ user: Pointer) -> Set where U: ParseUser { + getProtectedFields(user.objectId) + } + + /** + Get the protected fields for the given `ParseRole`. + + - parameter role: The `ParseRole` access to check. + - returns: The protected fields. + - throws: An error of type `ParseError`. + */ + func getProtectedFields(_ role: R) throws -> Set where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return getProtectedFields(roleNameAccess) + } + + /** + Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: Set, for objectId: String) -> Self { + setProtected(fields, on: \.protectedFields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return setProtectedFields(fields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + setProtectedFields(fields, for: user.objectId) + } + + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return setProtectedFields(fields, for: roleNameAccess) + } + + /** + Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for objectId: String) -> Self { + addProtected(fields, on: \.protectedFields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return addProtectedFields(fields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + addProtectedFields(fields, for: user.objectId) + } + + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func addProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return addProtectedFields(fields, for: roleNameAccess) + } + + /** + Remove the given `ParseUser` objectId is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for objectId: String) -> Self { + removeProtected(fields, on: \.protectedFields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { + let objectId = try user.toPointer().objectId + return removeProtectedFields(fields, for: objectId) + } + + /** + Set whether the given `ParseUser` is allowed to retrieve fields from this class. + + - parameter for: The `ParseUser` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { + removeProtectedFields(fields, for: user.objectId) + } + + /** + Set whether the given `ParseRole` is allowed to retrieve fields from this class. + + - parameter for: The `ParseRole` to provide/restrict access to. + - parameter fields: The fields to be protected. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func removeProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { + let roleNameAccess = try ParseACL.getRoleAccessName(role) + return removeProtectedFields(fields, for: roleNameAccess) + } +} + +// MARK: UserFields +extension ParseCLP { + func getUser(_ keyPath: KeyPath?>) -> Set { + self[keyPath: keyPath] ?? [] + } + + func setUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { + var mutableCLP = self + mutableCLP[keyPath: keyPath] = fields + return mutableCLP + } + + func addUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { + if let currentSet = self[keyPath: keyPath] { + var mutableCLP = self + mutableCLP[keyPath: keyPath] = currentSet.union(currentSet) + return mutableCLP + } else { + return setUser(keyPath, fields: fields) + } + } + + func removeUser(_ keyPath: WritableKeyPath?>, + fields: Set) -> Self { + var mutableCLP = self + fields.forEach { + mutableCLP[keyPath: keyPath]?.remove($0) + } + return mutableCLP + } +} + +// MARK: WriteUserFields +public extension ParseCLP { + + /** + Get the `writeUserFields`. + + - returns: User pointer fields. + */ + func getWriteUserFields() -> Set { + getUser(\.writeUserFields) + } + + /** + Sets permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setWriteUser(_ fields: Set) -> Self { + setUser(\.writeUserFields, fields: fields) + } + + /** + Adds permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addWriteUser(_ fields: Set) -> Self { + addUser(\.writeUserFields, fields: fields) + } + + /** + Adds permission for the user pointer fields or create/delete/update/addField operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeWriteUser(_ fields: Set) -> Self { + removeUser(\.writeUserFields, fields: fields) + } +} + +// MARK: ReadUserFields +public extension ParseCLP { + + /** + Get the `readUserFields`. + + - returns: User pointer fields. + */ + func getReadUserFields() -> Set { + getUser(\.readUserFields) + } + + /** + Sets permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func setReadUser(_ fields: Set) -> Self { + setUser(\.readUserFields, fields: fields) + } + + /** + Adds permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func addReadUser(_ fields: Set) -> Self { + addUser(\.readUserFields, fields: fields) + } + + /** + Removes permission for the user pointer fields or get/count/find operations. + + - parameter fields: User pointer fields. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ + func removeReadUser(_ fields: Set) -> Self { + removeUser(\.readUserFields, fields: fields) + } +} + // MARK: CustomDebugStringConvertible extension ParseCLP: CustomDebugStringConvertible { public var debugDescription: String { diff --git a/Sources/ParseSwift/Types/ParseCLPPointer.swift b/Sources/ParseSwift/Types/ParseCLPPointer.swift deleted file mode 100644 index 7f29ca7fa..000000000 --- a/Sources/ParseSwift/Types/ParseCLPPointer.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ParseCLPPointer.swift -// ParseSwift -// -// Created by Corey Baker on 5/27/22. -// Copyright © 2022 Parse Community. All rights reserved. -// - -import Foundation - -/// Class Level Permissions for `ParseSchema`. -public struct ParseCLPPointer: ParseClassLevelPermisioinable { - - public var get: [String: Set]? - public var find: [String: Set]? - public var count: [String: Set]? - public var create: [String: Set]? - public var update: [String: Set]? - public var delete: [String: Set]? - public var addField: [String: Set]? - public var protectedFields: [String: Set]? - public var readUserFields: Set? - public var writeUserFields: Set? - - /// Creates an empty CLP type. - public init() { } -} - -public extension ParseCLPPointer { - - func getPointer(_ keyPath: KeyPath]?>) -> Set { - self[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] ?? [] - } - - func setPointer(_ keyPath: WritableKeyPath]?>, - fields: Set) -> Self { - var mutableCLP = self - if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] = fields - } else { - mutableCLP[keyPath: keyPath] = [ParseCLP.Access.pointerFields.rawValue: fields] - } - return mutableCLP - } - - func addPointer(_ keyPath: WritableKeyPath]?>, - fields: Set) -> Self { - var mutableCLP = self - if let currentSet = mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] { - mutableCLP[keyPath: keyPath]?[ParseCLP.Access.pointerFields.rawValue] = currentSet.union(fields) - } else { - mutableCLP[keyPath: keyPath] = [ParseCLP.Access.pointerFields.rawValue: fields] - } - return mutableCLP - } -} diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 0f94f26b4..bbc777509 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -14,7 +14,7 @@ import Foundation use the master key in server-side applications where the key is kept secure and not exposed to the public. */ -public struct ParseSchema: ParseType, Decodable { +public struct ParseSchema: ParseType, Decodable { /// The class name of the `ParseSchema`. public var className: String @@ -26,7 +26,7 @@ public struct ParseSchema Date: Fri, 27 May 2022 22:44:28 -0400 Subject: [PATCH 22/55] working ParseSchema --- .../Contents.swift | 108 ++++++++++++++ Sources/ParseSwift/Types/ParseCLP.swift | 136 ++++++------------ Sources/ParseSwift/Types/ParseField.swift | 4 - Sources/ParseSwift/Types/ParseSchema.swift | 4 +- 4 files changed, 152 insertions(+), 100 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 0a94e29cd..7012da4b3 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -7,6 +7,36 @@ import ParseSwift PlaygroundPage.current.needsIndefiniteExecution = true initializeParse() +//: Youe specific _User value type. +struct User: ParseUser { + //: These are required by `ParseObject`. + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + //: These are required by `ParseUser`. + var username: String? + var email: String? + var emailVerified: Bool? + var password: String? + var authData: [String: [String: String]?]? + + //: Your custom keys. + var customKey: String? + + //: Implement your own version of merge + func merge(with object: Self) throws -> Self { + var updated = try mergeParse(with: object) + if updated.shouldRestoreKey(\.customKey, + original: object) { + updated.customKey = object.customKey + } + return updated + } +} + //: Create your own value typed `ParseObject`. struct GameScore2: ParseObject { //: These are required by ParseObject @@ -19,6 +49,7 @@ struct GameScore2: ParseObject { //: Your own properties. var points: Int? var data: ParseBytes? + var owner: User? //: Implement your own version of merge func merge(with object: Self) throws -> Self { @@ -27,6 +58,14 @@ struct GameScore2: ParseObject { original: object) { updated.points = object.points } + if updated.shouldRestoreKey(\.data, + original: object) { + updated.data = object.data + } + if updated.shouldRestoreKey(\.owner, + original: object) { + updated.owner = object.owner + } return updated } } @@ -58,6 +97,15 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: clp) type: .bytes, options: ParseFieldOptions(required: false, defauleValue: nil)) +do { + gameScoreSchema = try gameScoreSchema.addField("owner", + type: .pointer, + target: User(), + options: ParseFieldOptions(required: false, defauleValue: nil)) +} catch { + print("Can't add field: \(gameScoreSchema)") +} +/* //: Now lets create the schema on the server. gameScoreSchema.create { result in switch result { @@ -67,5 +115,65 @@ gameScoreSchema.create { result in print("Couldn't save schema: \(error)") } } +*/ +//: We can update the CLP to only allow access to users specified in the "owner" field. +var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) +gameScoreSchema.classLevelPermissions = clp2 + +//: Now lets create the schema on the server. +gameScoreSchema.update { result in + switch result { + case .success(let savedSchema): + print("Check GameScore2 in Dashboard. \(savedSchema)") + case .failure(let error): + print("Couldn't update schema: \(error)") + } +} + +//: You can fetch your schema from the server at anytime. +gameScoreSchema.fetch { result in + switch result { + case .success(let fetchedSchema): + print("The current schema is: \(fetchedSchema)") + case .failure(let error): + print("Couldn't fetch schema: \(error)") + } +} + +//: Now lets save a new object to the new schema. +var gameScore = GameScore2() +gameScore.points = 120 +gameScore.owner = User.current + +gameScore.save { result in + switch result { + case .success(let savedGameScore): + print("The saved GameScore is: \(savedGameScore)") + case .failure(let error): + print("Couldn't save schema: \(error)") + } +} + +//: You can delete all objects your schema by purging them. +gameScoreSchema.purge { result in + switch result { + case .success: + print("All objects have been purged from this schema.") + case .failure(let error): + print("Couldn't purge schema: \(error)") + } +} + +/*: As long as there's no data in your `ParseSchema` you can + delete the schema. +*/ + gameScoreSchema.delete { result in + switch result { + case .success: + print("The schema has been deleted.") + case .failure(let error): + print("Couldn't delete the schema: \(error)") + } +} //: [Next](@next) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 45792e1e1..5081e8cc0 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -22,8 +22,22 @@ public struct ParseCLP: Codable, Equatable { public var readUserFields: Set? public var writeUserFields: Set? + /// The avialable actions on a `ParseSchema`. public enum Action { - case get, find, count, create, update, delete, addField + /// Fetch `ParseObject`'s. + case get + /// Find `ParseObject`'s. + case find + /// Count `ParseObject`'s. + case count + /// Create new `ParseObject`'s. + case create + /// Update `ParseObject`'s. + case update + /// Delete `ParseObject`'s. + case delete + /// Add field to the `ParseSchema`. + case addField internal func keyPath() -> KeyPath { let keyPath: KeyPath @@ -105,43 +119,37 @@ public struct ParseCLP: Codable, Equatable { } func setPointer(_ fields: Set, - on keyPath: WritableKeyPath, - for entity: String) -> Self { + on keyPath: WritableKeyPath) -> Self { var mutableCLP = self let value = AnyCodable(fields) if mutableCLP[keyPath: keyPath] != nil { - mutableCLP[keyPath: keyPath]?[entity] = value + mutableCLP[keyPath: keyPath]?[Access.pointerFields.rawValue] = value } else { - mutableCLP[keyPath: keyPath] = [entity: value] + mutableCLP[keyPath: keyPath] = [Access.pointerFields.rawValue: value] } return mutableCLP } func addPointer(_ fields: Set, - on keyPath: WritableKeyPath, - for entity: String) -> Self { + on keyPath: WritableKeyPath) -> Self { - if let currentSet = self[keyPath: keyPath]?[entity]?.value as? Set { + if let currentSet = self[keyPath: keyPath]?[Access.pointerFields.rawValue]?.value as? Set { var mutableCLP = self - mutableCLP[keyPath: keyPath]?[entity] = AnyCodable(currentSet.union(fields)) + mutableCLP[keyPath: keyPath]?[Access.pointerFields.rawValue] = AnyCodable(currentSet.union(fields)) return mutableCLP } else { - return setPointer(fields, on: keyPath, for: entity) + return setPointer(fields, on: keyPath) } } func removePointer(_ fields: Set, - on keyPath: WritableKeyPath, - for entity: String) -> Self { - - if var currentSet = self[keyPath: keyPath]?[entity]?.value as? Set { - var mutableCLP = self + on keyPath: WritableKeyPath) -> Self { + var mutableCLP = self + if var currentSet = self[keyPath: keyPath]?[Access.pointerFields.rawValue]?.value as? Set { fields.forEach { currentSet.remove($0) } - mutableCLP[keyPath: keyPath]?[entity] = AnyCodable(currentSet) - return mutableCLP - } else { - return setPointer(fields, on: keyPath, for: entity) + mutableCLP[keyPath: keyPath]?[Access.pointerFields.rawValue] = AnyCodable(currentSet) } + return mutableCLP } } @@ -180,8 +188,8 @@ public extension ParseCLP { /** Checks if get/find/count/create/update/delete/addField actions currently have public access. - - parameter keyPath: Any of the following keyPaths that represent an - action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - parameter action: Any of the following action on a `ParseSchema`: + get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. */ func getPointerFields(_ action: Action) throws -> Set { @@ -190,8 +198,8 @@ public extension ParseCLP { /** Checks if get/find/count/create/update/delete/addField actions currently have public access. - - parameter keyPath: Any of the following keyPaths that represent an - action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - parameter action: Any of the following actions on a `ParseSchema`: + get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. */ func hasAccessPublic(_ action: Action) throws -> Bool { @@ -200,8 +208,7 @@ public extension ParseCLP { /** Checks if get/find/count/create/update/delete/addField actions currently requires authentication to access. - - parameter keyPath: Any of the following keyPaths that represent an - action on a `ParseSchema`: get/find/count/create/update/delete/addField. + - parameter action: Any of the following action on a `ParseSchema`: get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. - warning: Requires Parse Server 2.3.0+. */ @@ -270,82 +277,23 @@ public extension ParseCLP { return setAccess(allow, on: action.writableKeyPath(), for: roleNameAccess) } + /** + Set access to a **\_User** column or array **\_User** column in this Schema. + - warning: Requires Parse Server 3.1.1+. + */ func setPointerFields(_ action: Action, - to fields: Set, - for objectId: String) -> Self { - setPointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func setPointerFields(_ action: Action, - to fields: Set, - for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return setPointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func setPointerFields(_ action: Action, - to fields: Set, - for user: Pointer) -> Self where U: ParseUser { - setPointer(fields, on: action.writableKeyPath(), for: user.objectId) - } - - func setPointerFields(_ action: Action, - to fields: Set, - for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setPointer(fields, on: action.writableKeyPath(), for: roleNameAccess) + to fields: Set) -> Self { + setPointer(fields, on: action.writableKeyPath()) } func addPointerFields(_ fields: Set, - on action: Action, - for objectId: String) -> Self { - addPointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func addPointerFields(_ fields: Set, - on action: Action, - for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return addPointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func addPointerFields(_ fields: Set, - on action: Action, - for user: Pointer) -> Self where U: ParseUser { - addPointer(fields, on: action.writableKeyPath(), for: user.objectId) - } - - func addPointerFields(_ fields: Set, - on action: Action, - for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return addPointer(fields, on: action.writableKeyPath(), for: roleNameAccess) + on action: Action) -> Self { + addPointer(fields, on: action.writableKeyPath()) } func removePointerFields(_ fields: Set, - on action: Action, - for objectId: String) -> Self { - removePointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func removePointerFields(_ fields: Set, - on action: Action, - for user: U) throws -> Self where U: ParseUser { - let objectId = try user.toPointer().objectId - return removePointer(fields, on: action.writableKeyPath(), for: objectId) - } - - func removePointerFields(_ fields: Set, - on action: Action, - for user: Pointer) -> Self where U: ParseUser { - removePointer(fields, on: action.writableKeyPath(), for: user.objectId) - } - - func removePointerFields(_ fields: Set, - on action: Action, - for role: R) throws -> Self where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - return removePointer(fields, on: action.writableKeyPath(), for: roleNameAccess) + on action: Action) -> Self { + removePointer(fields, on: action.writableKeyPath()) } } diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index ce996b89d..320850695 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -15,10 +15,6 @@ struct ParseField: Codable { var defaultValue: AnyCodable? var targetClass: String? - enum CodingKeys: String, CodingKey { - case type, required - } - init(operation: Operation) { __op = operation } diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index bbc777509..93178327c 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -546,8 +546,8 @@ extension ParseSchema { options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { - try deleteCommand().executeAsync(options: options, - callbackQueue: callbackQueue) { result in + try purgeCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in switch result { case .success: From b4327f11ed3eca0b2777aa66798054bdd35fb2da Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Fri, 27 May 2022 23:36:24 -0400 Subject: [PATCH 23/55] fix schema index --- .../Contents.swift | 7 +++++-- Sources/ParseSwift/Types/ParseSchema.swift | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 7012da4b3..e6dc8f67c 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -105,7 +105,7 @@ do { } catch { print("Can't add field: \(gameScoreSchema)") } -/* + //: Now lets create the schema on the server. gameScoreSchema.create { result in switch result { @@ -115,11 +115,14 @@ gameScoreSchema.create { result in print("Couldn't save schema: \(error)") } } -*/ + //: We can update the CLP to only allow access to users specified in the "owner" field. var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) gameScoreSchema.classLevelPermissions = clp2 +//: In addition, we can add an index. +gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "points", index: 1) + //: Now lets create the schema on the server. gameScoreSchema.update { result in switch result { diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 93178327c..e454124b3 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -23,7 +23,7 @@ public struct ParseSchema: ParseType, Decodable { internal var fields: [String: ParseField]? /// The indexs of this `ParseSchema`. - internal var indexes: [String: AnyCodable]? + internal var indexes: [String: [String: AnyCodable]]? /// The CLPs of this `ParseSchema`. public var classLevelPermissions: ParseCLP? @@ -69,20 +69,22 @@ public extension ParseSchema { } /** - Add a Field to create/update a `ParseSchema`. + Add an index to create/update a `ParseSchema`. - parameter name: Name of the index that will be created/updated on Parse Server. - - parameter index: The `ParseIndex` to add that will be created/updated on Parse Server. + - parameter field: The **field** to apply the `ParseIndex` to. + - parameter index: The **index** to create. - returns: A mutated instance of `ParseSchema` for easy chaining. */ func addIndex(_ name: String, - index: ParseIndex) -> Self { + field: String, + index: Encodable) -> Self { var mutableSchema = self if mutableSchema.indexes != nil { - mutableSchema.indexes?[name] = AnyCodable(index) + mutableSchema.indexes?[name]?[field] = AnyCodable(index) } else { - mutableSchema.indexes = [name: AnyCodable(index)] + mutableSchema.indexes = [name: [field: AnyCodable(index)]] } return mutableSchema @@ -345,13 +347,13 @@ public extension ParseSchema { - parameter name: Name of the index that will be deleted on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. */ - func deleteIndex(_ name: String) -> Self { + func deleteIndex(_ name: String, field: String) -> Self { let index = AnyCodable(Delete()) var mutableSchema = self if mutableSchema.indexes != nil { - mutableSchema.indexes?[name] = index + mutableSchema.indexes?[name]?[field] = index } else { - mutableSchema.indexes = [name: index] + mutableSchema.indexes = [name: [field: index]] } return mutableSchema From c831fab1488f00cd2c1ec70e324bc2259a094e44 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 10:46:49 -0400 Subject: [PATCH 24/55] docs --- Sources/ParseSwift/Types/ParseCLP.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 5081e8cc0..8568d2daf 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -18,9 +18,12 @@ public struct ParseCLP: Codable, Equatable { var update: [String: AnyCodable]? var delete: [String: AnyCodable]? var addField: [String: AnyCodable]? - public var protectedFields: [String: Set]? - public var readUserFields: Set? - public var writeUserFields: Set? + /// The users, roles, and access level restrictions for particular fields on this class. + public internal(set) var protectedFields: [String: Set]? + /// The users and roles that can perform get/count/find actions on this class. + public internal(set) var readUserFields: Set? + /// The users and roles that can perform create/delete/update/addField actions on this class. + public internal(set) var writeUserFields: Set? /// The avialable actions on a `ParseSchema`. public enum Action { @@ -88,7 +91,7 @@ public struct ParseCLP: Codable, Equatable { case pointerFields } - /// Creates an empty CLP type. + /// Creates an empty instance of CLP. public init() { } func getPointerFields(_ keyPath: KeyPath) -> Set { @@ -156,6 +159,11 @@ public struct ParseCLP: Codable, Equatable { // MARK: Default Implementation public extension ParseCLP { + /** + Creates an empty instance of CLP. + - parameter requireAuthentication: Read/Write to this class requires users to be authenticated. + - parameter publicAccess:Read/Write to this class can be done by the public. + */ init(requireAuthentication: Bool, publicAccess: Bool) { let clp = setWriteAccessRequiresAuthentication(requireAuthentication) .setReadAccessRequiresAuthentication(requireAuthentication) From de6710ea9801bb0388202802cfc60d77c42eb557 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 11:32:55 -0400 Subject: [PATCH 25/55] allow SPI to build docs --- .spi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.spi.yml b/.spi.yml index c47517008..8514ea675 100644 --- a/.spi.yml +++ b/.spi.yml @@ -3,6 +3,7 @@ builder: configs: - platform: ios scheme: "ParseSwift (iOS)" + documentation_targets: ["ParseSwift (iOS)"] - platform: macos-xcodebuild scheme: "ParseSwift (macOS)" - platform: macos-xcodebuild-arm From 66717c46eca981bc06d18301ba09f9df986c173e Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 14:50:27 -0400 Subject: [PATCH 26/55] docs update --- Sources/ParseSwift/Types/ParseCLP.swift | 391 +++++++++++++++--------- 1 file changed, 241 insertions(+), 150 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 8568d2daf..a254d6031 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -18,11 +18,11 @@ public struct ParseCLP: Codable, Equatable { var update: [String: AnyCodable]? var delete: [String: AnyCodable]? var addField: [String: AnyCodable]? - /// The users, roles, and access level restrictions for particular fields on this class. + /// The users, roles, and access level restrictions who cannot access particular fields in a Parse class. public internal(set) var protectedFields: [String: Set]? - /// The users and roles that can perform get/count/find actions on this class. + /// The users and roles that can perform get/count/find actions on a Parse class. public internal(set) var readUserFields: Set? - /// The users and roles that can perform create/delete/update/addField actions on this class. + /// The users and roles that can perform create/delete/update/addField actions on a Parse class. public internal(set) var writeUserFields: Set? /// The avialable actions on a `ParseSchema`. @@ -160,9 +160,12 @@ public struct ParseCLP: Codable, Equatable { public extension ParseCLP { /** - Creates an empty instance of CLP. - - parameter requireAuthentication: Read/Write to this class requires users to be authenticated. - - parameter publicAccess:Read/Write to this class can be done by the public. + Creates an instance of CLP with particular access. + - parameter requireAuthentication: Read/Write to a Parse class requires users to be authenticated. + - parameter publicAccess:Read/Write to a Parse class can be done by the public. + - warning: Setting `requireAuthentication` and `publicAccess` does not give **addField** + access. You can set **addField** access after creating an instance of CLP. + - warning: Requires Parse Server 2.3.0+. */ init(requireAuthentication: Bool, publicAccess: Bool) { let clp = setWriteAccessRequiresAuthentication(requireAuthentication) @@ -172,41 +175,19 @@ public extension ParseCLP { self = clp } - init(objectId: String, canAddFied: Bool = false) { - let clp = setWriteAccess(true, - objectId: objectId, - canAddField: canAddFied) - .setReadAccess(true, objectId: objectId) - self = clp - } - - init(user: U, canAddFied: Bool = false) throws where U: ParseUser { - let objectId = try user.toPointer().objectId - self.init(objectId: objectId, canAddFied: canAddFied) - } - - init(user: Pointer, canAddFied: Bool = false) where U: ParseUser { - self.init(objectId: user.objectId, canAddFied: canAddFied) - } - - init(role: R, canAddFied: Bool = false) throws where R: ParseRole { - let roleNameAccess = try ParseACL.getRoleAccessName(role) - self.init(objectId: roleNameAccess, canAddFied: canAddFied) - } - /** - Checks if get/find/count/create/update/delete/addField actions currently have public access. - - parameter action: Any of the following action on a `ParseSchema`: + Retreive the fields in a Parse class that determine access for a specific `Action`. + - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - - returns: **true** if access is allowed, **false** otherwise. + - returns: The set of user fields given access to a particular `Action`. */ func getPointerFields(_ action: Action) throws -> Set { getPointerFields(action.keyPath()) } /** - Checks if get/find/count/create/update/delete/addField actions currently have public access. - - parameter action: Any of the following actions on a `ParseSchema`: + Checks if an `Action` on a Parse class has public access. + - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. */ @@ -215,8 +196,9 @@ public extension ParseCLP { } /** - Checks if get/find/count/create/update/delete/addField actions currently requires authentication to access. - - parameter action: Any of the following action on a `ParseSchema`: get/find/count/create/update/delete/addField. + Checks if an `Action` on a Parse class requires users to be authenticated to access. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. - warning: Requires Parse Server 2.3.0+. */ @@ -224,34 +206,76 @@ public extension ParseCLP { hasAccess(action.keyPath(), for: Access.requiresAuthentication.rawValue) } + /** + Checks if an `Action` on a Parse class provides access to a specific `objectId`. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter objectId: The `ParseUser` objectId to check. + - returns: **true** if access is allowed, **false** otherwise. + */ func hasAccess(_ action: Action, for objectId: String) throws -> Bool { return hasAccess(action.keyPath(), for: objectId) } + /** + Checks if an `Action` on a Parse class provides access to a specific `ParseUser`. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter user: The `ParseUser` to check. + - returns: **true** if access is allowed, **false** otherwise. + - throws: An error of type `ParseError`. + */ func hasAccess(_ action: Action, for user: U) throws -> Bool where U: ParseUser { let objectId = try user.toPointer().objectId return hasAccess(action.keyPath(), for: objectId) } + /** + Checks if an `Action` on a Parse class provides access to a specific `ParseUser` pointer. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter user: The `ParseUser` pointer to check. + - returns: **true** if access is allowed, **false** otherwise. + */ func hasAccess(_ action: Action, - for user: Pointer) throws -> Bool where U: ParseUser { + for user: Pointer) -> Bool where U: ParseUser { hasAccess(action.keyPath(), for: user.objectId) } + /** + Checks if an `Action` on a Parse class provides access to a specific `ParseRole`. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter role: The `ParseRole` to check. + - returns: **true** if access is allowed, **false** otherwise. + - throws: An error of type `ParseError`. + */ func hasAccess(_ action: Action, for role: R) throws -> Bool where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) return hasAccess(action.keyPath(), for: roleNameAccess) } + /** + Set/remove public access to an `Action` on a Parse class. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ func setAccessPublic(_ allow: Bool, on action: Action) -> Self { setAccess(allow, on: action.writableKeyPath(), for: Access.publicScope.rawValue) } /** + Set/remove require user authentication to access an `Action` on a Parse class. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - returns: A mutated instance of `ParseCLP` for easy chaining. - warning: Requires Parse Server 2.3.0+. */ func setAccessRequiresAuthentication(_ allow: Bool, @@ -259,12 +283,29 @@ public extension ParseCLP { setAccess(allow, on: action.writableKeyPath(), for: Access.requiresAuthentication.rawValue) } + /** + Set/remove access to an `Action` for a specific user objectId on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter objectId: The `ParseUser` objectId to add/remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ func setAccess(_ action: Action, to allow: Bool, for objectId: String) -> Self { setAccess(allow, on: action.writableKeyPath(), for: objectId) } + /** + Set/remove access to an `Action` for a specific user `ParseUser` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter objectId: The `ParseUser` to add/remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ func setAccess(_ action: Action, to allow: Bool, for user: U) throws -> Self where U: ParseUser { @@ -272,12 +313,29 @@ public extension ParseCLP { return setAccess(allow, on: action.writableKeyPath(), for: objectId) } + /** + Set/remove access to an `Action` for a specific user `ParseUser` pointer on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter user: The `ParseUser` pointer to add/remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + */ func setAccess(_ action: Action, to allow: Bool, for user: Pointer) -> Self where U: ParseUser { setAccess(allow, on: action.writableKeyPath(), for: user.objectId) } + /** + Set/remove access to an `Action` for a specific `ParseRole` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter allow: **true** to allow access , **false** to remove access. + - parameter objectId: The `ParseRole` to add/remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ func setAccess(_ action: Action, to allow: Bool, for role: R) throws -> Self where R: ParseRole { @@ -286,7 +344,14 @@ public extension ParseCLP { } /** - Set access to a **\_User** column or array **\_User** column in this Schema. + Give access to a set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to give access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method replaces the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. */ func setPointerFields(_ action: Action, @@ -294,11 +359,33 @@ public extension ParseCLP { setPointer(fields, on: action.writableKeyPath()) } + /** + Add access to an additional set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to add access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. + - warning: Requires Parse Server 3.1.1+. + */ func addPointerFields(_ fields: Set, on action: Action) -> Self { addPointer(fields, on: action.writableKeyPath()) } + /** + Remove access for the set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. + - warning: Requires Parse Server 3.1.1+. + */ func removePointerFields(_ fields: Set, on action: Action) -> Self { removePointer(fields, on: action.writableKeyPath()) @@ -320,33 +407,34 @@ public extension ParseCLP { } /** - Check whether authentication is required to write to this class. + Check whether user authentication is required to perform create/update/delete/addField actions on a Parse class. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. + - warning: Requires Parse Server 2.3.0+. */ func hasWriteAccessRequiresAuthentication(_ checkAddField: Bool = false) -> Bool { hasWriteAccess(Access.requiresAuthentication.rawValue, check: checkAddField) } /** - Check whether the public has access to write to this class. + Check whether the public has access to perform create/update/delete/addField actions on a Parse class. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. */ func hasWriteAccessPublic(_ checkAddField: Bool = false) -> Bool { hasWriteAccess(Access.publicScope.rawValue, check: checkAddField) } /** - Check whether the `ParseUser` objectId has access to write to this class. + Check whether a `ParseUser` objectId has access to perform create/update/delete/addField actions on a Parse class. - parameter objectId: The `ParseUser` objectId to check. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. - throws: An error of type `ParseError`. */ func hasWriteAccess(_ objectId: String, @@ -355,13 +443,13 @@ public extension ParseCLP { } /** - Check whether the `ParseUser` pointer has access to write to this class. + Check whether the `ParseUser` pointer has access to perform create/update/delete/addField actions on a Parse class. - parameter user: The `ParseUser` pointer to check. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. */ func hasWriteAccess(_ user: Pointer, checkAddField: Bool = false) -> Bool where U: ParseUser { @@ -369,13 +457,13 @@ public extension ParseCLP { } /** - Check whether the `ParseUser` has access to write to this class. + Check whether the `ParseUser` has access to perform create/update/delete/addField actions on a Parse class. - parameter user: The `ParseUser` to check. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. - throws: An error of type `ParseError`. */ func hasWriteAccess(_ user: U, @@ -385,13 +473,13 @@ public extension ParseCLP { } /** - Check whether the `ParseRole` has access to write to this class. + Check whether the `ParseRole` has access to perform create/update/delete/addField actions on a Parse class. - parameter role: The `ParseRole` to check. - parameter checkAddField: **true** if `addField` should be part of the check, **false** otherwise. Defaults to **false**. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. - throws: An error of type `ParseError`. */ func hasWriteAccess(_ role: R, @@ -401,12 +489,12 @@ public extension ParseCLP { } /** - Sets whether authentication is required to create/update/delete/addField this class. - + Sets whether user authentication is required to perform create/update/delete/addField actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. + - warning: Requires Parse Server 2.3.0+. */ func setWriteAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { @@ -414,11 +502,10 @@ public extension ParseCLP { } /** - Sets whether the public is allowed to create/update/delete/addField this class. - + Sets whether the public has access to perform create/update/delete/addField actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteAccessPublic(_ allow: Bool, @@ -427,12 +514,11 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` objectId is allowed to create/update/delete/addField to this class. - + Sets whether the given `ParseUser` objectId has access to perform create/update/delete/addField actions on a Parse class. - parameter objectId: The `ParseUser` objectId to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteAccess(_ allow: Bool, @@ -449,12 +535,11 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` is allowed to create/update/delete/addField to this class. - + Sets whether the given `ParseUser` has access to perform create/update/delete/addField actions on a Parse class. - parameter user: The `ParseUser` to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -466,12 +551,11 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser`pointer is allowed to create/update/delete/addField to this class. - + Sets whether the given `ParseUser`pointer has access to perform create/update/delete/addField actions on a Parse class. - parameter user: The `ParseUser` to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteAccess(_ allow: Bool, @@ -481,12 +565,11 @@ public extension ParseCLP { } /** - Sets whether the given `ParseRole` is allowed to create/update/delete/addField to this class. - + Sets whether the given `ParseRole` has access to perform create/update/delete/addField actions on a Parse class. - parameter role: The `ParseRole` to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -502,27 +585,28 @@ public extension ParseCLP { public extension ParseCLP { /** - Check whether authentication is required to read from this class. - - returns: **true** if authentication is required, **false** otherwise. + Check whether user authentication is required to perform get/find/count actions on a Parse class. + - returns: **true** if has access, **false** otherwise. + - warning: Requires Parse Server 2.3.0+. */ func hasReadAccessRequiresAuthentication() -> Bool { hasReadAccess(Access.requiresAuthentication.rawValue) } /** - Check whether the public has access to read from this class. - - returns: **true** if authentication is required, **false** otherwise. + Check whether the public has access to perform get/find/count actions on a Parse class. + - returns: **true** if has access, **false** otherwise. */ func hasReadAccessPublic() -> Bool { hasReadAccess(Access.publicScope.rawValue) } /** - Check whether the `ParseUser` objectId has access to read from this class. + Check whether the `ParseUser` objectId has access to perform get/find/count actions on a Parse class. - parameter objectId: The `ParseUser` objectId to check. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. */ func hasReadAccess(_ objectId: String) -> Bool { hasAccess(\.get, for: objectId) @@ -531,23 +615,23 @@ public extension ParseCLP { } /** - Check whether the `ParseUser` pointer has access to read from this class. + Check whether the `ParseUser` pointer has access to perform get/find/count actions on a Parse class. - parameter user: The `ParseUser` pointer to check. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. */ func hasReadAccess(_ user: Pointer) -> Bool where U: ParseUser { hasReadAccess(user.objectId) } /** - Check whether the `ParseUser` has access to read from this class. + Check whether the `ParseUser` has access to perform get/find/count actions on a Parse class. - parameter user: The `ParseUser` to check. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - throws: An error of type `ParseError`. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. */ func hasReadAccess(_ user: U) throws -> Bool where U: ParseUser { let objectId = try user.toPointer().objectId @@ -555,12 +639,12 @@ public extension ParseCLP { } /** - Check whether the `ParseRole` has access to read from this class. + Check whether the `ParseRole` has access to perform get/find/count actions on a Parse class. - parameter role: The `ParseRole` to check. - - returns: **true** if authentication is required, **false** otherwise. + - returns: **true** if has access, **false** otherwise. - throws: An error of type `ParseError`. - warning: Even if **false** is returned, the `ParseUser`/`ParseRole` may still - have access if they aare apart of a `ParseRole` that has access. + have access if they are apart of a `ParseRole` that has access. */ func hasReadAccess(_ role: R) throws -> Bool where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) @@ -568,12 +652,12 @@ public extension ParseCLP { } /** - Sets whether authentication is required to get/find/count this class. - + Sets whether authentication is required to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. - - parameter can: **true** if access should be allowed to `addField`, **false** otherwise. - Defaults to **false**. + - parameter canAddField: **true** if access should be allowed to `addField`, + **false** otherwise. Defaults to **false**. - returns: A mutated instance of `ParseCLP` for easy chaining. + - warning: Requires Parse Server 2.3.0+. */ func setReadAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { @@ -581,8 +665,7 @@ public extension ParseCLP { } /** - Sets whether the public is allowed to get/find/count this class. - + Sets whether the public has access to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. - returns: A mutated instance of `ParseCLP` for easy chaining. */ @@ -591,10 +674,9 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` is allowed to get/find/count this class. - - - parameter objectId: The `ParseUser` pointer to provide/restrict access to. + Sets whether the given `ParseUser` has access to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter objectId: The `ParseUser` pointer to provide/restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadAccess(_ allow: Bool, @@ -607,10 +689,9 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` is allowed to get/find/count this class. - - - parameter role: The `ParseUser` to provide/restrict access to. + Sets whether the given `ParseUser` has access to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter user: The `ParseUser` to provide/restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -621,10 +702,9 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` is allowed to get/find/count this class. - - - parameter role: The `ParseUser` pointer to provide/restrict access to. + Sets whether the given `ParseUser` has access to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter user: The `ParseUser` pointer to provide/restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadAccess(_ allow: Bool, @@ -633,10 +713,9 @@ public extension ParseCLP { } /** - Sets whether the given `ParseRole` is allowed to get/find/count this class. - - - parameter role: The `ParseRole` to provide/restrict access to. + Sets whether the given `ParseRole` has access to perform get/find/count actions on a Parse class. - parameter allow: **true** if access should be allowed, **false** otherwise. + - parameter role: The `ParseRole` to provide/restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -688,20 +767,35 @@ public extension ParseClassLevelPermisioinable { } /** - Get the protected fields for the given `ParseUser` objectId. - - - parameter user: The `ParseUser` objectId access to check. - - returns: The protected fields. + Get the fields the publc cannot access. + - returns: The set protected fields that cannot be accessed. + */ + func getPublicProtectedFields() -> Set { + getProtectedFields(Access.publicScope.rawValue) + } + + /** + Get the fields the users with authentication cannot access. + - returns: The set protected fields that cannot be accessed. + - warning: Requires Parse Server 2.3.0+. + */ + func getPublicProtectedFields() -> Set { + getProtectedFields(Access.requiresAuthentication.rawValue) + } + + /** + Get the protected fields the given `ParseUser` objectId cannot access. + - parameter objectId: The `ParseUser` objectId access to check. + - returns: The set protected fields that cannot be accessed. */ func getProtectedFields(_ objectId: String) -> Set { getProtected(\.protectedFields, for: objectId) } /** - Get the protected fields for the given `ParseUser`. - + Get the protected fields the given `ParseUser` cannot access. - parameter user: The `ParseUser` access to check. - - returns: The protected fields. + - returns: The set protected fields that cannot be accessed. - throws: An error of type `ParseError`. */ func getProtectedFields(_ user: U) throws -> Set where U: ParseUser { @@ -710,20 +804,18 @@ public extension ParseClassLevelPermisioinable { } /** - Get the protected fields for the given `ParseUser` pointer. - + Get the protected fields for the given `ParseUser` pointer cannot access. - parameter user: The `ParseUser` access to check. - - returns: The protected fields. + - returns: The set protected fields that cannot be accessed. */ func getProtectedFields(_ user: Pointer) -> Set where U: ParseUser { getProtectedFields(user.objectId) } /** - Get the protected fields for the given `ParseRole`. - + Get the protected fields the given `ParseRole` cannot access. - parameter role: The `ParseRole` access to check. - - returns: The protected fields. + - returns: The set protected fields that cannot be accessed. - throws: An error of type `ParseError`. */ func getProtectedFields(_ role: R) throws -> Set where R: ParseRole { @@ -732,10 +824,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. - - - parameter for: The `ParseUser` objectId to provide/restrict access to. - - parameter fields: The fields to be protected. + Set whether the given `ParseUser` objectId should not have access to specific fields of a Parse class. + - parameter for: The `ParseUser` objectId to restrict access to. + - parameter fields: The set protected fields that should not be accessed. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -744,9 +835,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -757,9 +848,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ @@ -768,9 +859,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. + Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - parameter for: The `ParseRole` to provide/restrict access to. + - parameter for: The `ParseRole` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -781,9 +872,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` objectId is allowed to retrieve fields from this class. + Set whether the given `ParseUser` objectId has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter for: The `ParseUser` objectId to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -793,9 +884,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -806,9 +897,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ @@ -817,9 +908,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. + Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - parameter for: The `ParseRole` to provide/restrict access to. + - parameter for: The `ParseRole` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -830,9 +921,9 @@ public extension ParseClassLevelPermisioinable { } /** - Remove the given `ParseUser` objectId is allowed to retrieve fields from this class. + Remove the given `ParseUser` objectId has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` objectId to provide/restrict access to. + - parameter for: The `ParseUser` objectId to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -842,9 +933,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. @@ -855,9 +946,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` is allowed to retrieve fields from this class. + Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - parameter for: The `ParseUser` to provide/restrict access to. + - parameter for: The `ParseUser` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. */ @@ -866,9 +957,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseRole` is allowed to retrieve fields from this class. + Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - parameter for: The `ParseRole` to provide/restrict access to. + - parameter for: The `ParseRole` to restrict access to. - parameter fields: The fields to be protected. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. From d7db46aa58fae154a031d07d42ce749e472a269b Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 15:11:11 -0400 Subject: [PATCH 27/55] more docs --- ParseSwift.playground/Sources/Common.swift | 4 +- Sources/ParseSwift/Types/ParseCLP.swift | 93 +++++++++++----------- 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index fb32c0785..214f8c327 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index a254d6031..8e4524a2b 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -383,7 +383,7 @@ public extension ParseCLP { - parameter fields: The set of `ParseUser` columns or array of `ParseUser` columns to remove access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method adds on to the current set of `fields` in the CLP. + - note: This method removes from the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. */ func removePointerFields(_ fields: Set, @@ -825,8 +825,8 @@ public extension ParseClassLevelPermisioinable { /** Set whether the given `ParseUser` objectId should not have access to specific fields of a Parse class. - - parameter for: The `ParseUser` objectId to restrict access to. - - parameter fields: The set protected fields that should not be accessed. + - parameter objectId: The `ParseUser` objectId to restrict access to. + - parameter fields: The set of fields that should be protected from access. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -835,10 +835,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Set whether the given `ParseUser` should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -848,10 +847,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Set whether the given `ParseUser` should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { @@ -859,10 +857,9 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseRole` to restrict access to. - - parameter fields: The fields to be protected. + Set whether the given `ParseRole` should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter role: The `ParseRole` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -870,26 +867,26 @@ public extension ParseClassLevelPermisioinable { let roleNameAccess = try ParseACL.getRoleAccessName(role) return setProtectedFields(fields, for: roleNameAccess) } - + /** - Set whether the given `ParseUser` objectId has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` objectId to restrict access to. - - parameter fields: The fields to be protected. + Add to the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter objectId: The `ParseUser` objectId to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. */ func addProtectedFields(_ fields: Set, for objectId: String) -> Self { addProtected(fields, on: \.protectedFields, for: objectId) } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Add to the set of specific fields the given `ParseUser` should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. */ func addProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId @@ -897,23 +894,23 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Add to the set of specific fields the given `ParseUser` pointer should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. */ func addProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { addProtectedFields(fields, for: user.objectId) } /** - Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseRole` to restrict access to. - - parameter fields: The fields to be protected. + Add to the set of specific fields the given `ParseRole` should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter role: The `ParseRole` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. */ func addProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) @@ -921,24 +918,24 @@ public extension ParseClassLevelPermisioinable { } /** - Remove the given `ParseUser` objectId has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` objectId to restrict access to. - - parameter fields: The fields to be protected. + Remove fields from the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter objectId: The `ParseUser` objectId to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. */ func removeProtectedFields(_ fields: Set, for objectId: String) -> Self { removeProtected(fields, on: \.protectedFields, for: objectId) } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Remove fields from the set of specific fields the given `ParseUser` should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. */ func removeProtectedFields(_ fields: Set, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId @@ -946,23 +943,23 @@ public extension ParseClassLevelPermisioinable { } /** - Set whether the given `ParseUser` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseUser` to restrict access to. - - parameter fields: The fields to be protected. + Remove fields from the set of specific fields the given `ParseUser` pointer should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method removes from the current set of `fields` in the CLP. */ func removeProtectedFields(_ fields: Set, for user: Pointer) -> Self where U: ParseUser { removeProtectedFields(fields, for: user.objectId) } /** - Set whether the given `ParseRole` has access to retrieve fields from a Parse class. - - - parameter for: The `ParseRole` to restrict access to. - - parameter fields: The fields to be protected. + Remove fields from the set of specific fields the given `ParseRole` should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter role: The `ParseRole` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. */ func removeProtectedFields(_ fields: Set, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) From 2b7bc35d5b06d4d11bf7646d25db20adaef7fa4e Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 15:33:11 -0400 Subject: [PATCH 28/55] Finish ParseCLP docs --- ParseSwift.playground/Sources/Common.swift | 4 +- ParseSwift.xcodeproj/project.pbxproj | 10 --- .../ParseClassLevelPermisioinable.swift | 15 ---- Sources/ParseSwift/Types/ParseCLP.swift | 70 +++++++++++-------- 4 files changed, 41 insertions(+), 58 deletions(-) delete mode 100644 Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index 214f8c327..fb32c0785 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 17a251ebf..9e255e1be 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -370,10 +370,6 @@ 709A14A6283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; 709A14A7283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; 709A14A8283AAF4C00BF85E5 /* ParseSchema+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */; }; - 709A14AB2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; - 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; - 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; - 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */; }; 709B40C1268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C2268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; 709B40C3268F999000ED2EAC /* IOS13Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709B40C0268F999000ED2EAC /* IOS13Tests.swift */; }; @@ -1007,7 +1003,6 @@ 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; - 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseClassLevelPermisioinable.swift; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; 709B98302556EC7400507778 /* ParseSwiftTeststvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTeststvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 709B98342556EC7400507778 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1451,7 +1446,6 @@ F97B45C524D9C6F200F4A88B /* Fetchable.swift */, 705A9A2E25991C1400B3547F /* Fileable.swift */, 70BC988F252A5B5C00FF3074 /* Objectable.swift */, - 709A14AA2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift */, 709A14902839A60600BF85E5 /* ParseIndex.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, @@ -2356,7 +2350,6 @@ F97B463324D9C74400F4A88B /* URLSession.swift in Sources */, F97B464E24D9C78B00F4A88B /* Add.swift in Sources */, 703B095326BF47FD005A112F /* ParseTwitter+async.swift in Sources */, - 709A14AB2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9890252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FE24D9C6F200F4A88B /* ParseFile.swift in Sources */, F97B45EE24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, @@ -2598,7 +2591,6 @@ F97B463424D9C74400F4A88B /* URLSession.swift in Sources */, F97B464F24D9C78B00F4A88B /* Add.swift in Sources */, 703B095426BF47FD005A112F /* ParseTwitter+async.swift in Sources */, - 709A14AC2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9891252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B45FF24D9C6F200F4A88B /* ParseFile.swift in Sources */, F97B45EF24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, @@ -2943,7 +2935,6 @@ F97B464924D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */, 703B095626BF47FD005A112F /* ParseTwitter+async.swift in Sources */, - 709A14AE2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9893252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465524D9C78C00F4A88B /* AddUnique.swift in Sources */, F97B464D24D9C78B00F4A88B /* Delete.swift in Sources */, @@ -3091,7 +3082,6 @@ F97B464824D9C78B00F4A88B /* ParseOperation.swift in Sources */, F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */, 703B095526BF47FD005A112F /* ParseTwitter+async.swift in Sources */, - 709A14AD2840FFDD00BF85E5 /* ParseClassLevelPermisioinable.swift in Sources */, 70BC9892252A5B5C00FF3074 /* Objectable.swift in Sources */, F97B465424D9C78C00F4A88B /* AddUnique.swift in Sources */, F97B464C24D9C78B00F4A88B /* Delete.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift b/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift deleted file mode 100644 index 23271819e..000000000 --- a/Sources/ParseSwift/Protocols/ParseClassLevelPermisioinable.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ParseClassLevelPermisioinable.swift -// ParseSwift -// -// Created by Corey Baker on 5/27/22. -// Copyright © 2022 Parse Community. All rights reserved. -// - -import Foundation - -public protocol ParseClassLevelPermisioinable: Codable, Equatable { - var protectedFields: [String: Set]? { get set } - var readUserFields: Set? { get set } - var writeUserFields: Set? { get set } -} diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 8e4524a2b..29a713b73 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -514,7 +514,8 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser` objectId has access to perform create/update/delete/addField actions on a Parse class. + Sets whether the given `ParseUser` objectId has access to perform + create/update/delete/addField actions on a Parse class. - parameter objectId: The `ParseUser` objectId to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - parameter canAddField: **true** if access should be allowed to `addField`, @@ -551,7 +552,8 @@ public extension ParseCLP { } /** - Sets whether the given `ParseUser`pointer has access to perform create/update/delete/addField actions on a Parse class. + Sets whether the given `ParseUser`pointer has access to perform + create/update/delete/addField actions on a Parse class. - parameter user: The `ParseUser` to provide/restrict access to. - parameter to: **true** if access should be allowed, **false** otherwise. - parameter canAddField: **true** if access should be allowed to `addField`, @@ -726,7 +728,7 @@ public extension ParseCLP { } // MARK: Protected -public extension ParseClassLevelPermisioinable { +public extension ParseCLP { internal func getProtected(_ keyPath: KeyPath]?>, for entity: String) -> Set { self[keyPath: keyPath]?[entity] ?? [] @@ -779,7 +781,7 @@ public extension ParseClassLevelPermisioinable { - returns: The set protected fields that cannot be accessed. - warning: Requires Parse Server 2.3.0+. */ - func getPublicProtectedFields() -> Set { + func getRequiresAuthenticationProtectedFields() -> Set { getProtectedFields(Access.requiresAuthentication.rawValue) } @@ -867,7 +869,7 @@ public extension ParseClassLevelPermisioinable { let roleNameAccess = try ParseACL.getRoleAccessName(role) return setProtectedFields(fields, for: roleNameAccess) } - + /** Add to the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. - parameter fields: The set of fields that should be protected from access. @@ -918,7 +920,8 @@ public extension ParseClassLevelPermisioinable { } /** - Remove fields from the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. + Remove fields from the set of specific fields the given `ParseUser` objectId + should not have access to on a Parse class. - parameter fields: The set of fields that should be removed from protected access. - parameter objectId: The `ParseUser` objectId to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. @@ -943,7 +946,8 @@ public extension ParseClassLevelPermisioinable { } /** - Remove fields from the set of specific fields the given `ParseUser` pointer should not have access to on a Parse class. + Remove fields from the set of specific fields the given `ParseUser` pointer + should not have access to on a Parse class. - parameter fields: The set of fields that should be removed from protected access. - parameter user: The `ParseUser` to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. @@ -1005,18 +1009,18 @@ extension ParseCLP { public extension ParseCLP { /** - Get the `writeUserFields`. - - - returns: User pointer fields. + Get the set of `ParseUser` and array `ParseUser` fields that can + perform create/update/delete/addField actions on this Parse class. + - returns: The set of `ParseUser` and array `ParseUser` fields. */ func getWriteUserFields() -> Set { getUser(\.writeUserFields) } /** - Sets permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. + Set the `ParseUser` and array `ParseUser` fields that can + perform create/update/delete/addField actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteUser(_ fields: Set) -> Self { @@ -1024,20 +1028,22 @@ public extension ParseCLP { } /** - Adds permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. + Add to the set of `ParseUser` and array `ParseUser` fields that can + perform create/update/delete/addField actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. */ func addWriteUser(_ fields: Set) -> Self { addUser(\.writeUserFields, fields: fields) } /** - Adds permission for the user pointer fields or create/delete/update/addField operations. - - - parameter fields: User pointer fields. + Remove fields from the set of `ParseUser` and array `ParseUser` fields that can + perform create/update/delete/addField actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method removes from the current set of `fields` in the CLP. */ func removeWriteUser(_ fields: Set) -> Self { removeUser(\.writeUserFields, fields: fields) @@ -1048,18 +1054,18 @@ public extension ParseCLP { public extension ParseCLP { /** - Get the `readUserFields`. - - - returns: User pointer fields. + Get the set of `ParseUser` and array `ParseUser` fields that can + perform get/find/count actions on this Parse class. + - returns: The set of `ParseUser` and array `ParseUser` fields. */ func getReadUserFields() -> Set { getUser(\.readUserFields) } /** - Sets permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. + Set the `ParseUser` and array `ParseUser` fields that can + perform get/find/count actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadUser(_ fields: Set) -> Self { @@ -1067,20 +1073,22 @@ public extension ParseCLP { } /** - Adds permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. + Add to the set of `ParseUser` and array `ParseUser` fields that can + perform get/find/count actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. */ func addReadUser(_ fields: Set) -> Self { addUser(\.readUserFields, fields: fields) } /** - Removes permission for the user pointer fields or get/count/find operations. - - - parameter fields: User pointer fields. + Remove fields from the set of `ParseUser` and array `ParseUser` fields that can + perform get/find/count actions on this Parse class. + - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method removes from the current set of `fields` in the CLP. */ func removeReadUser(_ fields: Set) -> Self { removeUser(\.readUserFields, fields: fields) From 5e149c49ce4617dd9c86afa844e6893bce65b19e Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 15:39:35 -0400 Subject: [PATCH 29/55] nits --- Sources/ParseSwift/Types/ParseCLP.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 29a713b73..ec2b823bc 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -25,11 +25,11 @@ public struct ParseCLP: Codable, Equatable { /// The users and roles that can perform create/delete/update/addField actions on a Parse class. public internal(set) var writeUserFields: Set? - /// The avialable actions on a `ParseSchema`. + /// The avialable actions to perform on a Parse class. public enum Action { /// Fetch `ParseObject`'s. case get - /// Find `ParseObject`'s. + /// Query for `ParseObject`'s. case find /// Count `ParseObject`'s. case count @@ -39,7 +39,7 @@ public struct ParseCLP: Codable, Equatable { case update /// Delete `ParseObject`'s. case delete - /// Add field to the `ParseSchema`. + /// Add fields to the Parse class. case addField internal func keyPath() -> KeyPath { From 07a963f40fad169bf5c41799d71a8441da4830ae Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 15:48:17 -0400 Subject: [PATCH 30/55] doc schema nits --- Sources/ParseSwift/Types/ParseSchema.swift | 70 +++++++++++----------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index e454124b3..436dc7c9a 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -71,7 +71,7 @@ public extension ParseSchema { /** Add an index to create/update a `ParseSchema`. - - parameter name: Name of the index that will be created/updated on Parse Server. + - parameter name: Name of the index that will be created/updated in the schema on Parse Server. - parameter field: The **field** to apply the `ParseIndex` to. - parameter index: The **index** to create. - returns: A mutated instance of `ParseSchema` for easy chaining. @@ -93,10 +93,10 @@ public extension ParseSchema { /** Add a Field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. @@ -118,9 +118,9 @@ public extension ParseSchema { /** Add a Field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -141,8 +141,8 @@ public extension ParseSchema { /** Add a String field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -154,8 +154,8 @@ public extension ParseSchema { /** Add a Number field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -167,8 +167,8 @@ public extension ParseSchema { /** Add a Boolean field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -180,8 +180,8 @@ public extension ParseSchema { /** Add a Date field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -193,8 +193,8 @@ public extension ParseSchema { /** Add a File field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -206,8 +206,8 @@ public extension ParseSchema { /** Add a GeoPoint field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -219,8 +219,8 @@ public extension ParseSchema { /** Add a Polygon field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -232,8 +232,8 @@ public extension ParseSchema { /** Add an Object field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -245,8 +245,8 @@ public extension ParseSchema { /** Add a Bytes field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -258,8 +258,8 @@ public extension ParseSchema { /** Add an Array field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -271,10 +271,10 @@ public extension ParseSchema { /** Add a Pointer field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. Defaults to **nil**. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. @@ -300,8 +300,8 @@ public extension ParseSchema { /** Add a Relation field to create/update a `ParseSchema`. - - parameter name: Name of the field that will be created/updated on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated on Parse Server. + - parameter name: Name of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. Defaults to **nil**. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. @@ -326,7 +326,7 @@ public extension ParseSchema { /** Delete a field in the `ParseSchema`. - - parameter name: Name of the field that will be deleted on Parse Server. + - parameter name: Name of the field that will be deleted in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. */ func deleteField(_ name: String) -> Self { @@ -344,7 +344,7 @@ public extension ParseSchema { /** Delete an index in the `ParseSchema`. - - parameter name: Name of the index that will be deleted on Parse Server. + - parameter name: Name of the index that will be deleted in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. */ func deleteIndex(_ name: String, field: String) -> Self { From 0764534f8ce4d76e0be62878afac0f860c06e8c6 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 16:09:05 -0400 Subject: [PATCH 31/55] Finished docs and added changelog --- CHANGELOG.md | 3 + .../Contents.swift | 3 +- ParseSwift.playground/contents.xcplayground | 1 - Sources/ParseSwift/Types/ParseSchema.swift | 153 ++---------------- 4 files changed, 22 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23189ca0e..ab26825f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.5.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +__New features__ +- Add ParseSchema, ParseCLP, and ParseFieldOptions. Should only be used when using the Swift SDK on a secured server ([#370](https://github.com/parse-community/Parse-Swift/pull/370)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 4.5.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.4.0...4.5.0) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index e6dc8f67c..496ada01d 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -167,7 +167,8 @@ gameScoreSchema.purge { result in } } -/*: As long as there's no data in your `ParseSchema` you can +/*: + As long as there's no data in your `ParseSchema` you can delete the schema. */ gameScoreSchema.delete { result in diff --git a/ParseSwift.playground/contents.xcplayground b/ParseSwift.playground/contents.xcplayground index b37eba70a..774a9ea88 100644 --- a/ParseSwift.playground/contents.xcplayground +++ b/ParseSwift.playground/contents.xcplayground @@ -20,6 +20,5 @@ - \ No newline at end of file diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 436dc7c9a..8304a20b3 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -59,10 +59,15 @@ public extension ParseSchema { SchemaObject.className } + /// Create an empty instance of `ParseSchema` type. init() { self.init(className: SchemaObject.className) } + /** + Create an empty instance of ParseSchema type with a specific CLP. + - parameter classLevelPermissions: The CLP access for this `ParseSchema`. + */ init(classLevelPermissions: ParseCLP) { self.init(className: SchemaObject.className) self.classLevelPermissions = classLevelPermissions @@ -95,8 +100,10 @@ public extension ParseSchema { - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in + the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in + the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. @@ -120,7 +127,8 @@ public extension ParseSchema { - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in + the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ @@ -138,143 +146,15 @@ public extension ParseSchema { return mutableSchema } - /** - Add a String field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addString(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .string, options: options) - } - - /** - Add a Number field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addNumber(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .number, options: options) - } - - /** - Add a Boolean field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addBoolean(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .boolean, options: options) - } - - /** - Add a Date field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addDate(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .date, options: options) - } - - /** - Add a File field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addFile(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .file, options: options) - } - - /** - Add a GeoPoint field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addGeoPoint(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .geoPoint, options: options) - } - - /** - Add a Polygon field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addPolygon(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .polygon, options: options) - } - - /** - Add an Object field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addObject(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .object, options: options) - } - - /** - Add a Bytes field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addBytes(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .bytes, options: options) - } - - /** - Add an Array field to create/update a `ParseSchema`. - - - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - - returns: A mutated instance of `ParseSchema` for easy chaining. - - warning: The use of `options` requires Parse Server 3.7.0+. - */ - func addArray(_ name: String, - options: ParseFieldOptions) -> Self { - addField(name, type: .array, options: options) - } - /** Add a Pointer field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in + the schema on Parse Server. Defaults to **nil**. - - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in + the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. @@ -301,7 +181,8 @@ public extension ParseSchema { Add a Relation field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. + - parameter target: The target `ParseObject` of the field that will be created/updated in + the schema on Parse Server. Defaults to **nil**. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. From 9f33fb841c0a9ae5c754cdba3606398f9261db0c Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 17:46:02 -0400 Subject: [PATCH 32/55] add deleteField to playgrounds --- .../Contents.swift | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 496ada01d..07a65944e 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -110,12 +110,28 @@ do { gameScoreSchema.create { result in switch result { case .success(let savedSchema): - print("Check GameScore2 in Dashboard. \(savedSchema)") + print("Check GameScore2 in Dashboard. \nThe created schema: \(savedSchema)") case .failure(let error): print("Couldn't save schema: \(error)") } } +/*: + Fields can also be deleted on a schema. Lets remove + the **data** field since it's not going being used. +*/ +gameScoreSchema = gameScoreSchema.deleteField("data") + +//: Next, we need to update the schema on the server with the changes. +gameScoreSchema.update { result in + switch result { + case .success(let updatedSchema): + print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") + case .failure(let error): + print("Couldn't update schema: \(error)") + } +} + //: We can update the CLP to only allow access to users specified in the "owner" field. var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) gameScoreSchema.classLevelPermissions = clp2 @@ -123,26 +139,16 @@ gameScoreSchema.classLevelPermissions = clp2 //: In addition, we can add an index. gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "points", index: 1) -//: Now lets create the schema on the server. +//: Next, we need to update the schema on the server with the changes. gameScoreSchema.update { result in switch result { - case .success(let savedSchema): - print("Check GameScore2 in Dashboard. \(savedSchema)") + case .success(let updatedSchema): + print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") case .failure(let error): print("Couldn't update schema: \(error)") } } -//: You can fetch your schema from the server at anytime. -gameScoreSchema.fetch { result in - switch result { - case .success(let fetchedSchema): - print("The current schema is: \(fetchedSchema)") - case .failure(let error): - print("Couldn't fetch schema: \(error)") - } -} - //: Now lets save a new object to the new schema. var gameScore = GameScore2() gameScore.points = 120 From c775f651289342bbb0bbb1b234145d533c1bc338 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 19:26:51 -0400 Subject: [PATCH 33/55] fix pending indexes --- .../Contents.swift | 26 ++++++++------- Sources/ParseSwift/Types/ParseSchema.swift | 33 ++++++++----------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 07a65944e..152aada49 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -116,28 +116,32 @@ gameScoreSchema.create { result in } } -/*: - Fields can also be deleted on a schema. Lets remove - the **data** field since it's not going being used. -*/ -gameScoreSchema = gameScoreSchema.deleteField("data") +//: We can update the CLP to only allow access to users specified in the "owner" field. +var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) +gameScoreSchema.classLevelPermissions = clp2 + +//: In addition, we can add an index. +gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "points", index: 1) //: Next, we need to update the schema on the server with the changes. gameScoreSchema.update { result in switch result { case .success(let updatedSchema): print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") + /*: + Updated the current gameScoreSchema with the newest. + */ + gameScoreSchema = updatedSchema case .failure(let error): print("Couldn't update schema: \(error)") } } -//: We can update the CLP to only allow access to users specified in the "owner" field. -var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) -gameScoreSchema.classLevelPermissions = clp2 - -//: In addition, we can add an index. -gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "points", index: 1) +/*: + Fields can also be deleted on a schema. Lets remove + the **data** field since it's not going being used. +*/ +gameScoreSchema = gameScoreSchema.deleteField("data") //: Next, we need to update the schema on the server with the changes. gameScoreSchema.update { result in diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 8304a20b3..bf6261e88 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -18,15 +18,15 @@ public struct ParseSchema: ParseType, Decodable { /// The class name of the `ParseSchema`. public var className: String - - /// The fiekds of this `ParseSchema`. + /// The CLPs of this `ParseSchema`. + public var classLevelPermissions: ParseCLP? internal var fields: [String: ParseField]? - - /// The indexs of this `ParseSchema`. internal var indexes: [String: [String: AnyCodable]]? + internal var pendingIndexes = [String: [String: AnyCodable]]() - /// The CLPs of this `ParseSchema`. - public var classLevelPermissions: ParseCLP? + enum CodingKeys: String, CodingKey { + case className, classLevelPermissions, fields, indexes + } /** Get the current fields for this `ParseSchema`. @@ -85,13 +85,7 @@ public extension ParseSchema { field: String, index: Encodable) -> Self { var mutableSchema = self - - if mutableSchema.indexes != nil { - mutableSchema.indexes?[name]?[field] = AnyCodable(index) - } else { - mutableSchema.indexes = [name: [field: AnyCodable(index)]] - } - + mutableSchema.pendingIndexes[name] = [field: AnyCodable(index)] return mutableSchema } @@ -231,12 +225,7 @@ public extension ParseSchema { func deleteIndex(_ name: String, field: String) -> Self { let index = AnyCodable(Delete()) var mutableSchema = self - if mutableSchema.indexes != nil { - mutableSchema.indexes?[name]?[field] = index - } else { - mutableSchema.indexes = [name: [field: index]] - } - + mutableSchema.pendingIndexes[name] = [field: index] return mutableSchema } } @@ -363,11 +352,15 @@ extension ParseSchema { public func update(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { + var mutableSchema = self + if !mutableSchema.pendingIndexes.isEmpty { + mutableSchema.indexes = pendingIndexes + } var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { - try updateCommand() + try mutableSchema.updateCommand() .executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) From 6322479ca5d86ed1c8f0094fc8cfcbf7d7b45d4b Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 19:55:58 -0400 Subject: [PATCH 34/55] improve protected fields --- .../Contents.swift | 28 ++++++++++- Sources/ParseSwift/Types/ParseCLP.swift | 48 ++++++++++++++++++- Sources/ParseSwift/Types/ParseSchema.swift | 2 + 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 152aada49..6ed6d7a3f 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -117,7 +117,7 @@ gameScoreSchema.create { result in } //: We can update the CLP to only allow access to users specified in the "owner" field. -var clp2 = clp.setPointerFields(.get, to: Set(["owner"])) +let clp2 = clp.setPointerFields(.get, to: Set(["owner"])) gameScoreSchema.classLevelPermissions = clp2 //: In addition, we can add an index. @@ -148,6 +148,32 @@ gameScoreSchema.update { result in switch result { case .success(let updatedSchema): print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") + /*: + Updated the current gameScoreSchema with the newest. + */ + gameScoreSchema = updatedSchema + case .failure(let error): + print("Couldn't update schema: \(error)") + } +} + +/*: + Fields can also be deleted on a schema. Lets remove + the **data** field since it's not going being used. +*/ +var clp3 = gameScoreSchema.classLevelPermissions +clp3 = clp3?.setProtectedFieldsPublic(["owner"]) +gameScoreSchema.classLevelPermissions = clp3 + +//: Next, we need to update the schema on the server with the changes. +gameScoreSchema.update { result in + switch result { + case .success(let updatedSchema): + print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") + /*: + Updated the current gameScoreSchema with the newest. + */ + gameScoreSchema = updatedSchema case .failure(let error): print("Couldn't update schema: \(error)") } diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index ec2b823bc..a3d6dc1d5 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -772,7 +772,7 @@ public extension ParseCLP { Get the fields the publc cannot access. - returns: The set protected fields that cannot be accessed. */ - func getPublicProtectedFields() -> Set { + func getProtectedFieldsPublic() -> Set { getProtectedFields(Access.publicScope.rawValue) } @@ -781,7 +781,7 @@ public extension ParseCLP { - returns: The set protected fields that cannot be accessed. - warning: Requires Parse Server 2.3.0+. */ - func getRequiresAuthenticationProtectedFields() -> Set { + func getProtectedFieldsRequireAuthentication() -> Set { getProtectedFields(Access.requiresAuthentication.rawValue) } @@ -825,6 +825,27 @@ public extension ParseCLP { return getProtectedFields(roleNameAccess) } + /** + Set whether the public should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFieldsPublic(_ fields: Set) -> Self { + setProtected(fields, on: \.protectedFields, for: Access.publicScope.rawValue) + } + + /** + Set whether authenticated users should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - warning: Requires Parse Server 2.3.0+. + */ + func setProtectedFieldsRequireAuthentication(_ fields: Set) -> Self { + setProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) + } + /** Set whether the given `ParseUser` objectId should not have access to specific fields of a Parse class. - parameter objectId: The `ParseUser` objectId to restrict access to. @@ -870,6 +891,29 @@ public extension ParseCLP { return setProtectedFields(fields, for: roleNameAccess) } + /** + Add to the set of specific fields the public should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. + */ + func addProtectedFieldsPublic(_ fields: Set) -> Self { + addProtected(fields, on: \.protectedFields, for: Access.publicScope.rawValue) + } + + /** + Add to the set of specific fields authenticated users should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. + - warning: Requires Parse Server 2.3.0+. + */ + func addProtectedFieldsRequireAuthentication(_ fields: Set) -> Self { + addProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) + } + /** Add to the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. - parameter fields: The set of fields that should be protected from access. diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index bf6261e88..14784a247 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -355,6 +355,8 @@ extension ParseSchema { var mutableSchema = self if !mutableSchema.pendingIndexes.isEmpty { mutableSchema.indexes = pendingIndexes + } else { + mutableSchema.indexes = nil } var options = options options.insert(.useMasterKey) From bce210a80fc5b6c6e3b16437d4179b6db6a1d2ac Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 20:14:05 -0400 Subject: [PATCH 35/55] test codecov --- .codecov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 1cc5f54a3..686232e07 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,10 +6,10 @@ coverage: status: patch: default: - target: auto + target: 2 changes: false project: default: - target: 87 + target: 82 comment: require_changes: true From a75014ef19880116cd8ed1c4767a1c853fb2ae27 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 20:52:10 -0400 Subject: [PATCH 36/55] revert codecov --- .codecov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 686232e07..1cc5f54a3 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,10 +6,10 @@ coverage: status: patch: default: - target: 2 + target: auto changes: false project: default: - target: 82 + target: 87 comment: require_changes: true From 160213931cf1ff700cae6a0b870dc882d90d809e Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sat, 28 May 2022 22:53:44 -0400 Subject: [PATCH 37/55] add some CLP tests --- .../Contents.swift | 2 +- ParseSwift.xcodeproj/project.pbxproj | 8 + Sources/ParseSwift/Types/ParseCLP.swift | 12 +- Tests/ParseSwiftTests/ParseCLPTests.swift | 636 ++++++++++++++++++ 4 files changed, 652 insertions(+), 6 deletions(-) create mode 100644 Tests/ParseSwiftTests/ParseCLPTests.swift diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 6ed6d7a3f..5dddcbb54 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -84,7 +84,7 @@ extension GameScore2 { } //: First lets create a new CLP for the new schema. -let clp = ParseCLP(requireAuthentication: true, publicAccess: false) +let clp = ParseCLP(requiresAuthentication: true, publicAccess: false) .setAccessPublic(true, on: .get) .setAccessPublic(true, on: .find) diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 9e255e1be..f54f76753 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -274,6 +274,9 @@ 7045769E26BD934000F86F71 /* ParseFile+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7045769C26BD934000F86F71 /* ParseFile+async.swift */; }; 7045769F26BD934000F86F71 /* ParseFile+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7045769C26BD934000F86F71 /* ParseFile+async.swift */; }; 704576A026BD934000F86F71 /* ParseFile+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7045769C26BD934000F86F71 /* ParseFile+async.swift */; }; + 705025992842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; + 7050259A2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; + 7050259B2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; 70510AAC259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; 70510AAD259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; 70510AAE259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; @@ -978,6 +981,7 @@ 7045769226BD8F8100F86F71 /* ParseInstallation+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseInstallation+async.swift"; sourceTree = ""; }; 7045769726BD917500F86F71 /* Query+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+async.swift"; sourceTree = ""; }; 7045769C26BD934000F86F71 /* ParseFile+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseFile+async.swift"; sourceTree = ""; }; + 705025982842FD3B008D6624 /* ParseCLPTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLPTests.swift; sourceTree = ""; }; 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveQuerySocket.swift; sourceTree = ""; }; 70572670259033A700F0ADD5 /* ParseFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileManager.swift; sourceTree = ""; }; 705727882593FF8000F0ADD5 /* ParseFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileTests.swift; sourceTree = ""; }; @@ -1288,6 +1292,7 @@ 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */, 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */, 70D1BE0625BB2BF400A42E7C /* ParseConfigTests.swift */, + 705025982842FD3B008D6624 /* ParseCLPTests.swift */, 91B40650267A66ED00B129CD /* ParseErrorTests.swift */, 917BA44D2703F2B400F8D747 /* ParseFacebookAsyncTests.swift */, 89899DB426045DC4002E2043 /* ParseFacebookCombineTests.swift */, @@ -2427,6 +2432,7 @@ 917BA42A2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */, 70BC0B33251903D1001556DB /* ParseGeoPointTests.swift in Sources */, 7003957625A0EE770052CB31 /* BatchUtilsTests.swift in Sources */, + 705025992842FD3B008D6624 /* ParseCLPTests.swift in Sources */, 705A99F9259807F900B3547F /* ParseFileManagerTests.swift in Sources */, 70F03A522780DA9200E5AFB4 /* ParseGoogleTests.swift in Sources */, 7044C20625C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */, @@ -2677,6 +2683,7 @@ 917BA42C2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */, 709B984F2556ECAA00507778 /* AnyCodableTests.swift in Sources */, 7003957825A0EE770052CB31 /* BatchUtilsTests.swift in Sources */, + 7050259B2842FD3B008D6624 /* ParseCLPTests.swift in Sources */, 705A99FB259807F900B3547F /* ParseFileManagerTests.swift in Sources */, 70F03A542780DA9200E5AFB4 /* ParseGoogleTests.swift in Sources */, 7044C20825C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */, @@ -2771,6 +2778,7 @@ 917BA42B2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */, 70F2E2BD254F283000B2EA5C /* AnyDecodableTests.swift in Sources */, 7003957725A0EE770052CB31 /* BatchUtilsTests.swift in Sources */, + 7050259A2842FD3B008D6624 /* ParseCLPTests.swift in Sources */, 705A99FA259807F900B3547F /* ParseFileManagerTests.swift in Sources */, 70F03A532780DA9200E5AFB4 /* ParseGoogleTests.swift in Sources */, 7044C20725C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */, diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index a3d6dc1d5..259d863ec 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -161,15 +161,15 @@ public extension ParseCLP { /** Creates an instance of CLP with particular access. - - parameter requireAuthentication: Read/Write to a Parse class requires users to be authenticated. + - parameter requiresAuthentication: Read/Write to a Parse class requires users to be authenticated. - parameter publicAccess:Read/Write to a Parse class can be done by the public. - - warning: Setting `requireAuthentication` and `publicAccess` does not give **addField** + - warning: Setting `requiresAuthentication` and `publicAccess` does not give **addField** access. You can set **addField** access after creating an instance of CLP. - warning: Requires Parse Server 2.3.0+. */ - init(requireAuthentication: Bool, publicAccess: Bool) { - let clp = setWriteAccessRequiresAuthentication(requireAuthentication) - .setReadAccessRequiresAuthentication(requireAuthentication) + init(requiresAuthentication: Bool, publicAccess: Bool) { + let clp = setWriteAccessRequiresAuthentication(requiresAuthentication) + .setReadAccessRequiresAuthentication(requiresAuthentication) .setWriteAccessPublic(publicAccess) .setReadAccessPublic(publicAccess) self = clp @@ -531,6 +531,8 @@ public extension ParseCLP { .setAccess(allow, on: \.delete, for: objectId) if addField { updatedCLP = updatedCLP.setAccess(allow, on: \.addField, for: objectId) + } else { + updatedCLP = updatedCLP.setAccess(false, on: \.addField, for: objectId) } return updatedCLP } diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift new file mode 100644 index 000000000..bda425219 --- /dev/null +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -0,0 +1,636 @@ +// +// ParseCLPTests.swift +// ParseSwift +// +// Created by Corey Baker on 5/28/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation +import XCTest +@testable import ParseSwift + +class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length + + struct User: ParseUser { + + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // These are required by ParseUser + var username: String? + var email: String? + var emailVerified: Bool? + var password: String? + var authData: [String: [String: String]?]? + + // Your custom keys + var customKey: String? + + init() { } + init(objectId: String) { + self.objectId = objectId + } + } + + struct Role: ParseRole { + + // required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // provided by Role + var name: String? + } + + override func setUpWithError() throws { + try super.setUpWithError() + guard let url = URL(string: "http://localhost:1337/1") else { + XCTFail("Should create valid URL") + return + } + ParseSwift.initialize(applicationId: "applicationId", + clientKey: "clientKey", + masterKey: "masterKey", + serverURL: url, + testing: true) + } + + let objectId = "1234" + let user = User(objectId: "1234") + + override func tearDownWithError() throws { + try super.tearDownWithError() + MockURLProtocol.removeAll() + #if !os(Linux) && !os(Android) && !os(Windows) + try KeychainStore.shared.deleteAll() + #endif + try ParseStorage.shared.deleteAll() + } + + func testCLPInitializerRequiresAuthentication() throws { + let clp = ParseCLP(requiresAuthentication: true, publicAccess: false) + XCTAssertEqual(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.create?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.count?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.protectedFields) + XCTAssertNil(clp.readUserFields) + XCTAssertNil(clp.writeUserFields) + XCTAssertNil(clp.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) + } + + func testCLPInitializerPublicAccess() throws { + let clp = ParseCLP(requiresAuthentication: false, publicAccess: true) + XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.create?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.count?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.protectedFields) + XCTAssertNil(clp.readUserFields) + XCTAssertNil(clp.writeUserFields) + XCTAssertNil(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + } + + func testCLPInitializerRequireAndPublicAccess() throws { + let clp = ParseCLP(requiresAuthentication: true, publicAccess: true) + XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.create?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.count?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.protectedFields) + XCTAssertNil(clp.readUserFields) + XCTAssertNil(clp.writeUserFields) + XCTAssertEqual(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.create?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.count?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + } + + func testCLPWriteAccessPublicSet() throws { + let clp = ParseCLP().setWriteAccessPublic(true) + XCTAssertNil(clp.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertEqual(clp.create?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) + + let clp2 = ParseCLP().setWriteAccessPublic(true, canAddField: true) + XCTAssertNil(clp2.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertEqual(clp2.create?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp2.update?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp2.delete?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp2.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertEqual(clp2.addField?[ParseCLP.Access.publicScope.rawValue], true) + + let clp3 = clp.setWriteAccessPublic(false) + XCTAssertNil(clp3.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.addField?[ParseCLP.Access.publicScope.rawValue]) + + let clp4 = clp2.setWriteAccessPublic(false) + XCTAssertNil(clp4.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp4.addField?[ParseCLP.Access.publicScope.rawValue]) + } + + func testCLPWriteAccessPublicSetEncode() throws { + let clp = ParseCLP().setWriteAccessPublic(true, canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"*\":true},\"create\":{\"*\":true},\"delete\":{\"*\":true},\"update\":{\"*\":true}})") + } + + func testCLPWriteAccessRequiresAuthenticationSet() throws { + let clp = ParseCLP().setWriteAccessRequiresAuthentication(true) + XCTAssertNil(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertEqual(clp.create?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.update?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.delete?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + + let clp2 = ParseCLP().setWriteAccessRequiresAuthentication(true, canAddField: true) + XCTAssertNil(clp2.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertEqual(clp2.create?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp2.update?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp2.delete?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp2.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertEqual(clp2.addField?[ParseCLP.Access.requiresAuthentication.rawValue], true) + + let clp3 = clp.setWriteAccessRequiresAuthentication(false) + XCTAssertNil(clp3.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + + let clp4 = clp2.setWriteAccessRequiresAuthentication(false) + XCTAssertNil(clp4.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp4.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + } + + func testCLPWriteAccessRequiresAuthenticationSetEncode() throws { + let clp = ParseCLP().setWriteAccessRequiresAuthentication(true, canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"requiresAuthentication\":true},\"create\":{\"requiresAuthentication\":true},\"delete\":{\"requiresAuthentication\":true},\"update\":{\"requiresAuthentication\":true}})") + } + + func testCLPWriteAccessObjectIdSet() throws { + let clp = ParseCLP().setWriteAccess(true, objectId: objectId) + XCTAssertNil(clp.get?[objectId]) + XCTAssertNil(clp.find?[objectId]) + XCTAssertEqual(clp.create?[objectId], true) + XCTAssertEqual(clp.update?[objectId], true) + XCTAssertEqual(clp.delete?[objectId], true) + XCTAssertNil(clp.count?[objectId]) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertEqual(clp2.create?[objectId], true) + XCTAssertEqual(clp2.update?[objectId], true) + XCTAssertEqual(clp2.delete?[objectId], true) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertEqual(clp2.addField?[objectId], true) + + let clp3 = clp.setWriteAccess(false, objectId: objectId) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + + let clp4 = clp2.setWriteAccess(false, objectId: objectId) + XCTAssertNil(clp4.get?[objectId]) + XCTAssertNil(clp4.find?[objectId]) + XCTAssertNil(clp4.create?[objectId]) + XCTAssertNil(clp4.update?[objectId]) + XCTAssertNil(clp4.delete?[objectId]) + XCTAssertNil(clp4.count?[objectId]) + XCTAssertNil(clp4.addField?[objectId]) + } + + func testCLPWriteAccessObjectIdSetEncode() throws { + let clp = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") + } + + func testCLPWriteAccessUserSet() throws { + let clp = try ParseCLP().setWriteAccess(true, user: user) + XCTAssertNil(clp.get?[objectId]) + XCTAssertNil(clp.find?[objectId]) + XCTAssertEqual(clp.create?[objectId], true) + XCTAssertEqual(clp.update?[objectId], true) + XCTAssertEqual(clp.delete?[objectId], true) + XCTAssertNil(clp.count?[objectId]) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertEqual(clp2.create?[objectId], true) + XCTAssertEqual(clp2.update?[objectId], true) + XCTAssertEqual(clp2.delete?[objectId], true) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertEqual(clp2.addField?[objectId], true) + + let clp3 = try clp.setWriteAccess(false, user: user) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + + let clp4 = try clp2.setWriteAccess(false, user: user) + XCTAssertNil(clp4.get?[objectId]) + XCTAssertNil(clp4.find?[objectId]) + XCTAssertNil(clp4.create?[objectId]) + XCTAssertNil(clp4.update?[objectId]) + XCTAssertNil(clp4.delete?[objectId]) + XCTAssertNil(clp4.count?[objectId]) + XCTAssertNil(clp4.addField?[objectId]) + } + + func testCLPWriteAccessUserSetEncode() throws { + let clp = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") + } + + func testCLPWriteAccessPointerSet() throws { + let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) + XCTAssertNil(clp.get?[objectId]) + XCTAssertNil(clp.find?[objectId]) + XCTAssertEqual(clp.create?[objectId], true) + XCTAssertEqual(clp.update?[objectId], true) + XCTAssertEqual(clp.delete?[objectId], true) + XCTAssertNil(clp.count?[objectId]) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = ParseCLP().setWriteAccess(true, + user: try user.toPointer(), + canAddField: true) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertEqual(clp2.create?[objectId], true) + XCTAssertEqual(clp2.update?[objectId], true) + XCTAssertEqual(clp2.delete?[objectId], true) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertEqual(clp2.addField?[objectId], true) + + let clp3 = clp.setWriteAccess(false, user: try user.toPointer()) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + + let clp4 = clp2.setWriteAccess(false, user: try user.toPointer()) + XCTAssertNil(clp4.get?[objectId]) + XCTAssertNil(clp4.find?[objectId]) + XCTAssertNil(clp4.create?[objectId]) + XCTAssertNil(clp4.update?[objectId]) + XCTAssertNil(clp4.delete?[objectId]) + XCTAssertNil(clp4.count?[objectId]) + XCTAssertNil(clp4.addField?[objectId]) + } + + func testCLPWriteAccessPointerSetEncode() throws { + let clp = ParseCLP().setWriteAccess(true, + user: try user.toPointer(), + canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") + } + + func testCLPWriteAccessRoleSet() throws { + let name = "hello" + let role = try Role(name: name) + let roleName = try ParseACL.getRoleAccessName(role) + let clp = try ParseCLP().setWriteAccess(true, role: role) + XCTAssertNil(clp.get?[roleName]) + XCTAssertNil(clp.find?[roleName]) + XCTAssertEqual(clp.create?[roleName], true) + XCTAssertEqual(clp.update?[roleName], true) + XCTAssertEqual(clp.delete?[roleName], true) + XCTAssertNil(clp.count?[roleName]) + XCTAssertNil(clp.addField?[roleName]) + + let clp2 = try ParseCLP().setWriteAccess(true, + role: role, + canAddField: true) + XCTAssertNil(clp2.get?[roleName]) + XCTAssertNil(clp2.find?[roleName]) + XCTAssertEqual(clp2.create?[roleName], true) + XCTAssertEqual(clp2.update?[roleName], true) + XCTAssertEqual(clp2.delete?[roleName], true) + XCTAssertNil(clp2.count?[roleName]) + XCTAssertEqual(clp2.addField?[roleName], true) + + let clp3 = try clp.setWriteAccess(false, role: role) + XCTAssertNil(clp3.get?[roleName]) + XCTAssertNil(clp3.find?[roleName]) + XCTAssertNil(clp3.create?[roleName]) + XCTAssertNil(clp3.update?[roleName]) + XCTAssertNil(clp3.delete?[roleName]) + XCTAssertNil(clp3.count?[roleName]) + XCTAssertNil(clp3.addField?[roleName]) + + let clp4 = try clp2.setWriteAccess(false, role: role) + XCTAssertNil(clp4.get?[roleName]) + XCTAssertNil(clp4.find?[roleName]) + XCTAssertNil(clp4.create?[roleName]) + XCTAssertNil(clp4.update?[roleName]) + XCTAssertNil(clp4.delete?[roleName]) + XCTAssertNil(clp4.count?[roleName]) + XCTAssertNil(clp4.addField?[roleName]) + } + + func testCLPWriteAccessRoleSetEncode() throws { + let name = "hello" + let role = try Role(name: name) + let roleName = try ParseACL.getRoleAccessName(role) + let clp = try ParseCLP().setWriteAccess(true, + role: role, + canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(roleName)\":true},\"create\":{\"\(roleName)\":true},\"delete\":{\"\(roleName)\":true},\"update\":{\"\(roleName)\":true}})") + } + + func testCLPReadAccessPublicSet() throws { + let clp = ParseCLP().setReadAccessPublic(true) + XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertEqual(clp.count?[ParseCLP.Access.publicScope.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) + + let clp2 = ParseCLP().setReadAccessPublic(false) + XCTAssertNil(clp2.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp2.addField?[ParseCLP.Access.publicScope.rawValue]) + + let clp3 = clp.setReadAccessPublic(false) + XCTAssertNil(clp3.get?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.find?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.create?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.update?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.delete?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.count?[ParseCLP.Access.publicScope.rawValue]) + XCTAssertNil(clp3.addField?[ParseCLP.Access.publicScope.rawValue]) + } + + func testCLPReadAccessPublicSetEncode() throws { + let clp = ParseCLP().setReadAccessPublic(true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"*\":true},\"find\":{\"*\":true},\"get\":{\"*\":true}})") + } + + func testCLPReadAccessRequiresAuthenticationSet() throws { + let clp = ParseCLP().setReadAccessRequiresAuthentication(true) + XCTAssertEqual(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertEqual(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertEqual(clp.count?[ParseCLP.Access.requiresAuthentication.rawValue], true) + XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + + let clp2 = ParseCLP().setReadAccessRequiresAuthentication(false) + XCTAssertNil(clp2.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp2.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + + let clp3 = clp.setReadAccessRequiresAuthentication(false) + XCTAssertNil(clp3.get?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.find?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.create?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.update?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.delete?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.count?[ParseCLP.Access.requiresAuthentication.rawValue]) + XCTAssertNil(clp3.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) + } + + func testCLPReadAccessRequiresAuthenticationSetEncode() throws { + let clp = ParseCLP().setReadAccessRequiresAuthentication(true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"requiresAuthentication\":true},\"find\":{\"requiresAuthentication\":true},\"get\":{\"requiresAuthentication\":true}})") + } + + func testCLPReadAccessObjectIdSet() throws { + let clp = ParseCLP().setReadAccess(true, objectId: objectId) + XCTAssertEqual(clp.get?[objectId], true) + XCTAssertEqual(clp.find?[objectId], true) + XCTAssertNil(clp.create?[objectId]) + XCTAssertNil(clp.update?[objectId]) + XCTAssertNil(clp.delete?[objectId]) + XCTAssertEqual(clp.count?[objectId], true) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = ParseCLP().setReadAccess(false, objectId: objectId) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertNil(clp2.create?[objectId]) + XCTAssertNil(clp2.update?[objectId]) + XCTAssertNil(clp2.delete?[objectId]) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertNil(clp2.addField?[objectId]) + + let clp3 = clp.setReadAccess(false, objectId: objectId) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + } + + func testCLPReadAccessObjectIdSetEncode() throws { + let clp = ParseCLP().setReadAccess(true, objectId: objectId) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") + } + + func testCLPReadAccessUserSet() throws { + let clp = try ParseCLP().setReadAccess(true, user: user) + XCTAssertEqual(clp.get?[objectId], true) + XCTAssertEqual(clp.find?[objectId], true) + XCTAssertNil(clp.create?[objectId]) + XCTAssertNil(clp.update?[objectId]) + XCTAssertNil(clp.delete?[objectId]) + XCTAssertEqual(clp.count?[objectId], true) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = try ParseCLP().setReadAccess(false, user: user) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertNil(clp2.create?[objectId]) + XCTAssertNil(clp2.update?[objectId]) + XCTAssertNil(clp2.delete?[objectId]) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertNil(clp2.addField?[objectId]) + + let clp3 = try clp.setReadAccess(false, user: user) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + } + + func testCLPReadAccessUserSetEncode() throws { + let clp = try ParseCLP().setReadAccess(true, user: user) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") + } + + func testCLPReadAccessPointerSet() throws { + let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) + XCTAssertEqual(clp.get?[objectId], true) + XCTAssertEqual(clp.find?[objectId], true) + XCTAssertNil(clp.create?[objectId]) + XCTAssertNil(clp.update?[objectId]) + XCTAssertNil(clp.delete?[objectId]) + XCTAssertEqual(clp.count?[objectId], true) + XCTAssertNil(clp.addField?[objectId]) + + let clp2 = ParseCLP().setReadAccess(false, user: try user.toPointer()) + XCTAssertNil(clp2.get?[objectId]) + XCTAssertNil(clp2.find?[objectId]) + XCTAssertNil(clp2.create?[objectId]) + XCTAssertNil(clp2.update?[objectId]) + XCTAssertNil(clp2.delete?[objectId]) + XCTAssertNil(clp2.count?[objectId]) + XCTAssertNil(clp2.addField?[objectId]) + + let clp3 = clp.setReadAccess(false, user: try user.toPointer()) + XCTAssertNil(clp3.get?[objectId]) + XCTAssertNil(clp3.find?[objectId]) + XCTAssertNil(clp3.create?[objectId]) + XCTAssertNil(clp3.update?[objectId]) + XCTAssertNil(clp3.delete?[objectId]) + XCTAssertNil(clp3.count?[objectId]) + XCTAssertNil(clp3.addField?[objectId]) + } + + func testCLPReadAccessPointerSetEncode() throws { + let clp = ParseCLP().setReadAccess(true, + user: try user.toPointer()) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") + } + + func testCLPReadAccessRoleSet() throws { + let name = "hello" + let role = try Role(name: name) + let roleName = try ParseACL.getRoleAccessName(role) + let clp = try ParseCLP().setReadAccess(true, role: role) + XCTAssertEqual(clp.get?[roleName], true) + XCTAssertEqual(clp.find?[roleName], true) + XCTAssertNil(clp.create?[roleName]) + XCTAssertNil(clp.update?[roleName]) + XCTAssertNil(clp.delete?[roleName]) + XCTAssertEqual(clp.count?[roleName], true) + XCTAssertNil(clp.addField?[roleName]) + + let clp2 = try ParseCLP().setReadAccess(false, role: role) + XCTAssertNil(clp2.get?[roleName]) + XCTAssertNil(clp2.find?[roleName]) + XCTAssertNil(clp2.create?[roleName]) + XCTAssertNil(clp2.update?[roleName]) + XCTAssertNil(clp2.delete?[roleName]) + XCTAssertNil(clp2.count?[roleName]) + XCTAssertNil(clp2.addField?[roleName]) + + let clp3 = try clp.setReadAccess(false, role: role) + XCTAssertNil(clp3.get?[roleName]) + XCTAssertNil(clp3.find?[roleName]) + XCTAssertNil(clp3.create?[roleName]) + XCTAssertNil(clp3.update?[roleName]) + XCTAssertNil(clp3.delete?[roleName]) + XCTAssertNil(clp3.count?[roleName]) + XCTAssertNil(clp3.addField?[roleName]) + } + + func testCLPReadAccessRoleSetEncode() throws { + let name = "hello" + let role = try Role(name: name) + let roleName = try ParseACL.getRoleAccessName(role) + let clp = try ParseCLP().setReadAccess(true, + role: role) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(roleName)\":true},\"find\":{\"\(roleName)\":true},\"get\":{\"\(roleName)\":true}})") + } +} From 78d2c0581dfe53659c0c68608b4c8567d9fcb808 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 09:22:50 -0400 Subject: [PATCH 38/55] refactor --- .../Contents.swift | 2 +- Sources/ParseSwift/Types/ParseCLP.swift | 36 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 5dddcbb54..8b75679ca 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -117,7 +117,7 @@ gameScoreSchema.create { result in } //: We can update the CLP to only allow access to users specified in the "owner" field. -let clp2 = clp.setPointerFields(.get, to: Set(["owner"])) +let clp2 = clp.setPointerFields(Set(["owner"]), on: .get) gameScoreSchema.classLevelPermissions = clp2 //: In addition, we can add an index. diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 259d863ec..a2c7b40ac 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -291,8 +291,8 @@ public extension ParseCLP { - parameter objectId: The `ParseUser` objectId to add/remove access to. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setAccess(_ action: Action, - to allow: Bool, + func setAccess(_ allow: Bool, + on action: Action, for objectId: String) -> Self { setAccess(allow, on: action.writableKeyPath(), for: objectId) } @@ -306,8 +306,8 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setAccess(_ action: Action, - to allow: Bool, + func setAccess(_ allow: Bool, + on action: Action, for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId return setAccess(allow, on: action.writableKeyPath(), for: objectId) @@ -321,8 +321,8 @@ public extension ParseCLP { - parameter user: The `ParseUser` pointer to add/remove access to. - returns: A mutated instance of `ParseCLP` for easy chaining. */ - func setAccess(_ action: Action, - to allow: Bool, + func setAccess(_ allow: Bool, + on action: Action, for user: Pointer) -> Self where U: ParseUser { setAccess(allow, on: action.writableKeyPath(), for: user.objectId) } @@ -336,8 +336,8 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setAccess(_ action: Action, - to allow: Bool, + func setAccess(_ allow: Bool, + on action: Action, for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) return setAccess(allow, on: action.writableKeyPath(), for: roleNameAccess) @@ -354,8 +354,8 @@ public extension ParseCLP { - note: This method replaces the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. */ - func setPointerFields(_ action: Action, - to fields: Set) -> Self { + func setPointerFields(_ fields: Set, + on action: Action) -> Self { setPointer(fields, on: action.writableKeyPath()) } @@ -526,13 +526,13 @@ public extension ParseCLP { objectId: String, canAddField addField: Bool = false) -> Self { var updatedCLP = self - .setAccess(allow, on: \.create, for: objectId) - .setAccess(allow, on: \.update, for: objectId) - .setAccess(allow, on: \.delete, for: objectId) + .setAccess(allow, on: .create, for: objectId) + .setAccess(allow, on: .update, for: objectId) + .setAccess(allow, on: .delete, for: objectId) if addField { - updatedCLP = updatedCLP.setAccess(allow, on: \.addField, for: objectId) + updatedCLP = updatedCLP.setAccess(allow, on: .addField, for: objectId) } else { - updatedCLP = updatedCLP.setAccess(false, on: \.addField, for: objectId) + updatedCLP = updatedCLP.setAccess(false, on: .addField, for: objectId) } return updatedCLP } @@ -686,9 +686,9 @@ public extension ParseCLP { func setReadAccess(_ allow: Bool, objectId: String) -> Self { let updatedCLP = self - .setAccess(allow, on: \.get, for: objectId) - .setAccess(allow, on: \.find, for: objectId) - .setAccess(allow, on: \.count, for: objectId) + .setAccess(allow, on: .get, for: objectId) + .setAccess(allow, on: .find, for: objectId) + .setAccess(allow, on: .count, for: objectId) return updatedCLP } From 5b729c4a71322d4d17abd91fadb07f704147dcfb Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 10:03:24 -0400 Subject: [PATCH 39/55] more CLP tests --- ParseSwift.xcodeproj/project.pbxproj | 4 +- Tests/ParseSwiftTests/ParseCLPTests.swift | 172 ++++++++++++++++++++++ 2 files changed, 174 insertions(+), 2 deletions(-) diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index f54f76753..5d55ad04c 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -1272,9 +1272,9 @@ 709B40C0268F999000ED2EAC /* IOS13Tests.swift */, 4AA8076E1F794C1C008CD551 /* KeychainStoreTests.swift */, 9194657724F16E330070296B /* ParseACLTests.swift */, - 70170A4D2656EBA50070C905 /* ParseAnalyticsTests.swift */, 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */, 91CB9536265966DF0043E5D6 /* ParseAnalyticsCombineTests.swift */, + 70170A4D2656EBA50070C905 /* ParseAnalyticsTests.swift */, 917BA4452703EEA700F8D747 /* ParseAnonymousAsyncTests.swift */, 7044C22C25C5E4E90011F6E7 /* ParseAnonymousCombineTests.swift */, 70A2D86A25B3ADB6001BEB7D /* ParseAnonymousTests.swift */, @@ -1289,10 +1289,10 @@ 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */, 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */, 91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */, + 705025982842FD3B008D6624 /* ParseCLPTests.swift */, 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */, 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */, 70D1BE0625BB2BF400A42E7C /* ParseConfigTests.swift */, - 705025982842FD3B008D6624 /* ParseCLPTests.swift */, 91B40650267A66ED00B129CD /* ParseErrorTests.swift */, 917BA44D2703F2B400F8D747 /* ParseFacebookAsyncTests.swift */, 89899DB426045DC4002E2043 /* ParseFacebookCombineTests.swift */, diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index bda425219..22a631a4d 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -416,6 +416,92 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(roleName)\":true},\"create\":{\"\(roleName)\":true},\"delete\":{\"\(roleName)\":true},\"update\":{\"\(roleName)\":true}})") } + func testCLPWriteAccessPublicHas() throws { + let clp = ParseCLP().setWriteAccessPublic(true) + XCTAssertTrue(clp.hasWriteAccessPublic()) + XCTAssertFalse(clp.hasWriteAccessRequiresAuthentication()) + + let clp2 = ParseCLP().setWriteAccessPublic(false) + XCTAssertFalse(clp2.hasWriteAccessPublic()) + XCTAssertFalse(clp2.hasWriteAccessRequiresAuthentication()) + + let clp3 = clp.setWriteAccessPublic(false) + XCTAssertFalse(clp3.hasWriteAccessPublic()) + XCTAssertFalse(clp3.hasWriteAccessRequiresAuthentication()) + } + + func testCLPWriteAccessRequiresAuthenticationHas() throws { + let clp = ParseCLP().setWriteAccessRequiresAuthentication(true) + XCTAssertTrue(clp.hasWriteAccessRequiresAuthentication()) + XCTAssertFalse(clp.hasWriteAccessPublic()) + + let clp2 = ParseCLP().setWriteAccessRequiresAuthentication(false) + XCTAssertFalse(clp2.hasWriteAccessRequiresAuthentication()) + XCTAssertFalse(clp2.hasWriteAccessPublic()) + + let clp3 = clp.setWriteAccessRequiresAuthentication(false) + XCTAssertFalse(clp3.hasWriteAccessRequiresAuthentication()) + XCTAssertFalse(clp3.hasWriteAccessPublic()) + } + + func testCLPWriteAccessObjectIdHas() throws { + let clp = ParseCLP().setWriteAccess(true, objectId: objectId) + XCTAssertFalse(clp.hasReadAccess(objectId)) + XCTAssertTrue(clp.hasWriteAccess(objectId)) + + let clp2 = ParseCLP().setWriteAccess(false, objectId: objectId) + XCTAssertFalse(clp2.hasReadAccess(objectId)) + XCTAssertFalse(clp2.hasWriteAccess(objectId)) + + let clp3 = clp.setWriteAccess(false, objectId: objectId) + XCTAssertFalse(clp3.hasReadAccess(objectId)) + XCTAssertFalse(clp3.hasWriteAccess(objectId)) + } + + func testCLPWriteAccessUserHas() throws { + let clp = try ParseCLP().setWriteAccess(true, user: user) + XCTAssertFalse(try clp.hasReadAccess(user)) + XCTAssertTrue(try clp.hasWriteAccess(user)) + + let clp2 = try ParseCLP().setWriteAccess(false, user: user) + XCTAssertFalse(try clp2.hasReadAccess(user)) + XCTAssertFalse(try clp2.hasWriteAccess(user)) + + let clp3 = try clp.setWriteAccess(false, user: user) + XCTAssertFalse(try clp3.hasReadAccess(user)) + XCTAssertFalse(try clp3.hasWriteAccess(user)) + } + + func testCLPWriteAccessPointerHas() throws { + let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) + XCTAssertFalse(clp.hasReadAccess(try user.toPointer())) + XCTAssertTrue(clp.hasWriteAccess(try user.toPointer())) + + let clp2 = ParseCLP().setWriteAccess(false, user: try user.toPointer()) + XCTAssertFalse(clp2.hasReadAccess(try user.toPointer())) + XCTAssertFalse(clp2.hasWriteAccess(try user.toPointer())) + + let clp3 = clp.setWriteAccess(false, user: try user.toPointer()) + XCTAssertFalse(clp3.hasReadAccess(try user.toPointer())) + XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) + } + + func testCLPWriteAccessRoleHas() throws { + let name = "hello" + let role = try Role(name: name) + let clp = try ParseCLP().setWriteAccess(true, role: role) + XCTAssertFalse(try clp.hasReadAccess(role)) + XCTAssertTrue(try clp.hasWriteAccess(role)) + + let clp2 = try ParseCLP().setWriteAccess(false, role: role) + XCTAssertFalse(try clp2.hasReadAccess(role)) + XCTAssertFalse(try clp2.hasWriteAccess(role)) + + let clp3 = try clp.setWriteAccess(false, role: role) + XCTAssertFalse(try clp3.hasReadAccess(role)) + XCTAssertFalse(try clp3.hasWriteAccess(role)) + } + func testCLPReadAccessPublicSet() throws { let clp = ParseCLP().setReadAccessPublic(true) XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) @@ -633,4 +719,90 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(roleName)\":true},\"find\":{\"\(roleName)\":true},\"get\":{\"\(roleName)\":true}})") } + + func testCLPReadAccessPublicHas() throws { + let clp = ParseCLP().setReadAccessPublic(true) + XCTAssertTrue(clp.hasReadAccessPublic()) + XCTAssertFalse(clp.hasReadAccessRequiresAuthentication()) + + let clp2 = ParseCLP().setReadAccessPublic(false) + XCTAssertFalse(clp2.hasReadAccessPublic()) + XCTAssertFalse(clp2.hasReadAccessRequiresAuthentication()) + + let clp3 = clp.setReadAccessPublic(false) + XCTAssertFalse(clp3.hasReadAccessPublic()) + XCTAssertFalse(clp3.hasReadAccessRequiresAuthentication()) + } + + func testCLPReadAccessRequiresAuthenticationHas() throws { + let clp = ParseCLP().setReadAccessRequiresAuthentication(true) + XCTAssertTrue(clp.hasReadAccessRequiresAuthentication()) + XCTAssertFalse(clp.hasReadAccessPublic()) + + let clp2 = ParseCLP().setReadAccessRequiresAuthentication(false) + XCTAssertFalse(clp2.hasReadAccessRequiresAuthentication()) + XCTAssertFalse(clp2.hasReadAccessPublic()) + + let clp3 = clp.setReadAccessRequiresAuthentication(false) + XCTAssertFalse(clp3.hasReadAccessRequiresAuthentication()) + XCTAssertFalse(clp3.hasReadAccessPublic()) + } + + func testCLPReadAccessObjectIdHas() throws { + let clp = ParseCLP().setReadAccess(true, objectId: objectId) + XCTAssertTrue(clp.hasReadAccess(objectId)) + XCTAssertFalse(clp.hasWriteAccess(objectId)) + + let clp2 = ParseCLP().setReadAccess(false, objectId: objectId) + XCTAssertFalse(clp2.hasReadAccess(objectId)) + XCTAssertFalse(clp2.hasWriteAccess(objectId)) + + let clp3 = clp.setReadAccess(false, objectId: objectId) + XCTAssertFalse(clp3.hasReadAccess(objectId)) + XCTAssertFalse(clp3.hasWriteAccess(objectId)) + } + + func testCLPReadAccessUserHas() throws { + let clp = try ParseCLP().setReadAccess(true, user: user) + XCTAssertTrue(try clp.hasReadAccess(user)) + XCTAssertFalse(try clp.hasWriteAccess(user)) + + let clp2 = try ParseCLP().setReadAccess(false, user: user) + XCTAssertFalse(try clp2.hasReadAccess(user)) + XCTAssertFalse(try clp2.hasWriteAccess(user)) + + let clp3 = try clp.setReadAccess(false, user: user) + XCTAssertFalse(try clp3.hasReadAccess(user)) + XCTAssertFalse(try clp3.hasWriteAccess(user)) + } + + func testCLPReadAccessPointerHas() throws { + let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) + XCTAssertTrue(clp.hasReadAccess(try user.toPointer())) + XCTAssertFalse(clp.hasWriteAccess(try user.toPointer())) + + let clp2 = ParseCLP().setReadAccess(false, user: try user.toPointer()) + XCTAssertFalse(clp2.hasReadAccess(try user.toPointer())) + XCTAssertFalse(clp2.hasWriteAccess(try user.toPointer())) + + let clp3 = clp.setReadAccess(false, user: try user.toPointer()) + XCTAssertFalse(clp3.hasReadAccess(try user.toPointer())) + XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) + } + + func testCLPReadAccessRoleHas() throws { + let name = "hello" + let role = try Role(name: name) + let clp = try ParseCLP().setReadAccess(true, role: role) + XCTAssertTrue(try clp.hasReadAccess(role)) + XCTAssertFalse(try clp.hasWriteAccess(role)) + + let clp2 = try ParseCLP().setReadAccess(false, role: role) + XCTAssertFalse(try clp2.hasReadAccess(role)) + XCTAssertFalse(try clp2.hasWriteAccess(role)) + + let clp3 = try clp.setReadAccess(false, role: role) + XCTAssertFalse(try clp3.hasReadAccess(role)) + XCTAssertFalse(try clp3.hasWriteAccess(role)) + } } From e6b6ee749feb65e6693a6eacbeb68b35b679cf42 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 10:08:19 -0400 Subject: [PATCH 40/55] call sights --- Sources/ParseSwift/Types/ParseCLP.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index a2c7b40ac..b513ebf2b 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -214,7 +214,7 @@ public extension ParseCLP { - returns: **true** if access is allowed, **false** otherwise. */ func hasAccess(_ action: Action, - for objectId: String) throws -> Bool { + for objectId: String) -> Bool { return hasAccess(action.keyPath(), for: objectId) } @@ -397,11 +397,11 @@ public extension ParseCLP { internal func hasWriteAccess(_ entity: String, check addField: Bool) -> Bool { - let access = hasAccess(\.create, for: entity) - && hasAccess(\.update, for: entity) - && hasAccess(\.delete, for: entity) + let access = hasAccess(.create, for: entity) + && hasAccess(.update, for: entity) + && hasAccess(.delete, for: entity) if addField { - return access && hasAccess(\.addField, for: entity) + return access && hasAccess(.addField, for: entity) } return access } @@ -613,9 +613,9 @@ public extension ParseCLP { have access if they are apart of a `ParseRole` that has access. */ func hasReadAccess(_ objectId: String) -> Bool { - hasAccess(\.get, for: objectId) - && hasAccess(\.find, for: objectId) - && hasAccess(\.count, for: objectId) + hasAccess(.get, for: objectId) + && hasAccess(.find, for: objectId) + && hasAccess(.count, for: objectId) } /** From 6f40c7e971ce86b14dda6bc8219cc68b524df570 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 10:57:36 -0400 Subject: [PATCH 41/55] more tests --- Tests/ParseSwiftTests/ParseCLPTests.swift | 124 +++++++++++++++------- 1 file changed, 85 insertions(+), 39 deletions(-) diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 22a631a4d..12758777c 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -75,7 +75,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length try ParseStorage.shared.deleteAll() } - func testCLPInitializerRequiresAuthentication() throws { + func testInitializerRequiresAuthentication() throws { let clp = ParseCLP(requiresAuthentication: true, publicAccess: false) XCTAssertEqual(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue], true) XCTAssertEqual(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue], true) @@ -96,7 +96,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[ParseCLP.Access.publicScope.rawValue]) } - func testCLPInitializerPublicAccess() throws { + func testInitializerPublicAccess() throws { let clp = ParseCLP(requiresAuthentication: false, publicAccess: true) XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) @@ -117,7 +117,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } - func testCLPInitializerRequireAndPublicAccess() throws { + func testInitializerRequireAndPublicAccess() throws { let clp = ParseCLP(requiresAuthentication: true, publicAccess: true) XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) @@ -138,7 +138,33 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } - func testCLPWriteAccessPublicSet() throws { + func testAccessUser() throws { + let clp = try ParseCLP().setAccess(true, on: .create, for: user) + XCTAssertTrue(try clp.hasAccess(.create, for: user)) + + let clp2 = try clp.setAccess(false, on: .create, for: user) + XCTAssertFalse(try clp2.hasAccess(.create, for: user)) + } + + func testAccessPointer() throws { + let user = try user.toPointer() + let clp = ParseCLP().setAccess(true, on: .create, for: user) + XCTAssertTrue(clp.hasAccess(.create, for: user)) + + let clp2 = clp.setAccess(false, on: .create, for: user) + XCTAssertFalse(clp2.hasAccess(.create, for: user)) + } + + func testAccessRole() throws { + let role = try Role(name: "hello") + let clp = try ParseCLP().setAccess(true, on: .create, for: role) + XCTAssertTrue(try clp.hasAccess(.create, for: role)) + + let clp2 = try clp.setAccess(false, on: .create, for: role) + XCTAssertFalse(try clp2.hasAccess(.create, for: user)) + } + + func testWriteAccessPublicSet() throws { let clp = ParseCLP().setWriteAccessPublic(true) XCTAssertNil(clp.get?[ParseCLP.Access.publicScope.rawValue]) XCTAssertNil(clp.find?[ParseCLP.Access.publicScope.rawValue]) @@ -176,13 +202,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[ParseCLP.Access.publicScope.rawValue]) } - func testCLPWriteAccessPublicSetEncode() throws { + func testWriteAccessPublicSetEncode() throws { let clp = ParseCLP().setWriteAccessPublic(true, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"*\":true},\"create\":{\"*\":true},\"delete\":{\"*\":true},\"update\":{\"*\":true}})") } - func testCLPWriteAccessRequiresAuthenticationSet() throws { + func testWriteAccessRequiresAuthenticationSet() throws { let clp = ParseCLP().setWriteAccessRequiresAuthentication(true) XCTAssertNil(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue]) XCTAssertNil(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue]) @@ -220,13 +246,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } - func testCLPWriteAccessRequiresAuthenticationSetEncode() throws { + func testWriteAccessRequiresAuthenticationSetEncode() throws { let clp = ParseCLP().setWriteAccessRequiresAuthentication(true, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"requiresAuthentication\":true},\"create\":{\"requiresAuthentication\":true},\"delete\":{\"requiresAuthentication\":true},\"update\":{\"requiresAuthentication\":true}})") } - func testCLPWriteAccessObjectIdSet() throws { + func testWriteAccessObjectIdSet() throws { let clp = ParseCLP().setWriteAccess(true, objectId: objectId) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) @@ -264,13 +290,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[objectId]) } - func testCLPWriteAccessObjectIdSetEncode() throws { + func testWriteAccessObjectIdSetEncode() throws { let clp = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") } - func testCLPWriteAccessUserSet() throws { + func testWriteAccessUserSet() throws { let clp = try ParseCLP().setWriteAccess(true, user: user) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) @@ -308,13 +334,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[objectId]) } - func testCLPWriteAccessUserSetEncode() throws { + func testWriteAccessUserSetEncode() throws { let clp = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") } - func testCLPWriteAccessPointerSet() throws { + func testWriteAccessPointerSet() throws { let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) @@ -354,7 +380,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[objectId]) } - func testCLPWriteAccessPointerSetEncode() throws { + func testWriteAccessPointerSetEncode() throws { let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer(), canAddField: true) @@ -362,7 +388,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") } - func testCLPWriteAccessRoleSet() throws { + func testWriteAccessRoleSet() throws { let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) @@ -405,7 +431,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp4.addField?[roleName]) } - func testCLPWriteAccessRoleSetEncode() throws { + func testWriteAccessRoleSetEncode() throws { let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) @@ -416,7 +442,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(roleName)\":true},\"create\":{\"\(roleName)\":true},\"delete\":{\"\(roleName)\":true},\"update\":{\"\(roleName)\":true}})") } - func testCLPWriteAccessPublicHas() throws { + func testWriteAccessPublicHas() throws { let clp = ParseCLP().setWriteAccessPublic(true) XCTAssertTrue(clp.hasWriteAccessPublic()) XCTAssertFalse(clp.hasWriteAccessRequiresAuthentication()) @@ -430,7 +456,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasWriteAccessRequiresAuthentication()) } - func testCLPWriteAccessRequiresAuthenticationHas() throws { + func testWriteAccessRequiresAuthenticationHas() throws { let clp = ParseCLP().setWriteAccessRequiresAuthentication(true) XCTAssertTrue(clp.hasWriteAccessRequiresAuthentication()) XCTAssertFalse(clp.hasWriteAccessPublic()) @@ -444,10 +470,11 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasWriteAccessPublic()) } - func testCLPWriteAccessObjectIdHas() throws { + func testWriteAccessObjectIdHas() throws { let clp = ParseCLP().setWriteAccess(true, objectId: objectId) XCTAssertFalse(clp.hasReadAccess(objectId)) XCTAssertTrue(clp.hasWriteAccess(objectId)) + XCTAssertFalse(clp.hasWriteAccess(objectId, check: true)) let clp2 = ParseCLP().setWriteAccess(false, objectId: objectId) XCTAssertFalse(clp2.hasReadAccess(objectId)) @@ -456,12 +483,17 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp3 = clp.setWriteAccess(false, objectId: objectId) XCTAssertFalse(clp3.hasReadAccess(objectId)) XCTAssertFalse(clp3.hasWriteAccess(objectId)) + + let clp4 = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + XCTAssertFalse(clp4.hasReadAccess(objectId)) + XCTAssertTrue(clp4.hasWriteAccess(objectId, check: true)) } - func testCLPWriteAccessUserHas() throws { + func testWriteAccessUserHas() throws { let clp = try ParseCLP().setWriteAccess(true, user: user) XCTAssertFalse(try clp.hasReadAccess(user)) XCTAssertTrue(try clp.hasWriteAccess(user)) + XCTAssertFalse(try clp.hasWriteAccess(user, checkAddField: true)) let clp2 = try ParseCLP().setWriteAccess(false, user: user) XCTAssertFalse(try clp2.hasReadAccess(user)) @@ -470,12 +502,17 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp3 = try clp.setWriteAccess(false, user: user) XCTAssertFalse(try clp3.hasReadAccess(user)) XCTAssertFalse(try clp3.hasWriteAccess(user)) + + let clp4 = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + XCTAssertFalse(try clp4.hasReadAccess(user)) + XCTAssertTrue(try clp4.hasWriteAccess(user, checkAddField: true)) } - func testCLPWriteAccessPointerHas() throws { + func testWriteAccessPointerHas() throws { let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) XCTAssertFalse(clp.hasReadAccess(try user.toPointer())) XCTAssertTrue(clp.hasWriteAccess(try user.toPointer())) + XCTAssertFalse(clp.hasWriteAccess(try user.toPointer(), checkAddField: true)) let clp2 = ParseCLP().setWriteAccess(false, user: try user.toPointer()) XCTAssertFalse(clp2.hasReadAccess(try user.toPointer())) @@ -484,14 +521,19 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp3 = clp.setWriteAccess(false, user: try user.toPointer()) XCTAssertFalse(clp3.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) + + let clp4 = ParseCLP().setWriteAccess(true, user: try user.toPointer(), canAddField: true) + XCTAssertFalse(clp4.hasReadAccess(try user.toPointer())) + XCTAssertTrue(clp4.hasWriteAccess(try user.toPointer(), checkAddField: true)) } - func testCLPWriteAccessRoleHas() throws { + func testWriteAccessRoleHas() throws { let name = "hello" let role = try Role(name: name) let clp = try ParseCLP().setWriteAccess(true, role: role) XCTAssertFalse(try clp.hasReadAccess(role)) XCTAssertTrue(try clp.hasWriteAccess(role)) + XCTAssertFalse(try clp.hasWriteAccess(role, checkAddField: true)) let clp2 = try ParseCLP().setWriteAccess(false, role: role) XCTAssertFalse(try clp2.hasReadAccess(role)) @@ -500,9 +542,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp3 = try clp.setWriteAccess(false, role: role) XCTAssertFalse(try clp3.hasReadAccess(role)) XCTAssertFalse(try clp3.hasWriteAccess(role)) + + let clp4 = try ParseCLP().setWriteAccess(true, role: role, canAddField: true) + XCTAssertFalse(try clp4.hasReadAccess(role)) + XCTAssertTrue(try clp4.hasWriteAccess(role, checkAddField: true)) } - func testCLPReadAccessPublicSet() throws { + func testReadAccessPublicSet() throws { let clp = ParseCLP().setReadAccessPublic(true) XCTAssertEqual(clp.get?[ParseCLP.Access.publicScope.rawValue], true) XCTAssertEqual(clp.find?[ParseCLP.Access.publicScope.rawValue], true) @@ -531,13 +577,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[ParseCLP.Access.publicScope.rawValue]) } - func testCLPReadAccessPublicSetEncode() throws { + func testReadAccessPublicSetEncode() throws { let clp = ParseCLP().setReadAccessPublic(true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"*\":true},\"find\":{\"*\":true},\"get\":{\"*\":true}})") } - func testCLPReadAccessRequiresAuthenticationSet() throws { + func testReadAccessRequiresAuthenticationSet() throws { let clp = ParseCLP().setReadAccessRequiresAuthentication(true) XCTAssertEqual(clp.get?[ParseCLP.Access.requiresAuthentication.rawValue], true) XCTAssertEqual(clp.find?[ParseCLP.Access.requiresAuthentication.rawValue], true) @@ -566,13 +612,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } - func testCLPReadAccessRequiresAuthenticationSetEncode() throws { + func testReadAccessRequiresAuthenticationSetEncode() throws { let clp = ParseCLP().setReadAccessRequiresAuthentication(true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"requiresAuthentication\":true},\"find\":{\"requiresAuthentication\":true},\"get\":{\"requiresAuthentication\":true}})") } - func testCLPReadAccessObjectIdSet() throws { + func testReadAccessObjectIdSet() throws { let clp = ParseCLP().setReadAccess(true, objectId: objectId) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) @@ -601,13 +647,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[objectId]) } - func testCLPReadAccessObjectIdSetEncode() throws { + func testReadAccessObjectIdSetEncode() throws { let clp = ParseCLP().setReadAccess(true, objectId: objectId) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } - func testCLPReadAccessUserSet() throws { + func testReadAccessUserSet() throws { let clp = try ParseCLP().setReadAccess(true, user: user) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) @@ -636,13 +682,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[objectId]) } - func testCLPReadAccessUserSetEncode() throws { + func testReadAccessUserSetEncode() throws { let clp = try ParseCLP().setReadAccess(true, user: user) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } - func testCLPReadAccessPointerSet() throws { + func testReadAccessPointerSet() throws { let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) @@ -671,14 +717,14 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[objectId]) } - func testCLPReadAccessPointerSetEncode() throws { + func testReadAccessPointerSetEncode() throws { let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } - func testCLPReadAccessRoleSet() throws { + func testReadAccessRoleSet() throws { let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) @@ -710,7 +756,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.addField?[roleName]) } - func testCLPReadAccessRoleSetEncode() throws { + func testReadAccessRoleSetEncode() throws { let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) @@ -720,7 +766,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(roleName)\":true},\"find\":{\"\(roleName)\":true},\"get\":{\"\(roleName)\":true}})") } - func testCLPReadAccessPublicHas() throws { + func testReadAccessPublicHas() throws { let clp = ParseCLP().setReadAccessPublic(true) XCTAssertTrue(clp.hasReadAccessPublic()) XCTAssertFalse(clp.hasReadAccessRequiresAuthentication()) @@ -734,7 +780,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasReadAccessRequiresAuthentication()) } - func testCLPReadAccessRequiresAuthenticationHas() throws { + func testReadAccessRequiresAuthenticationHas() throws { let clp = ParseCLP().setReadAccessRequiresAuthentication(true) XCTAssertTrue(clp.hasReadAccessRequiresAuthentication()) XCTAssertFalse(clp.hasReadAccessPublic()) @@ -748,7 +794,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasReadAccessPublic()) } - func testCLPReadAccessObjectIdHas() throws { + func testReadAccessObjectIdHas() throws { let clp = ParseCLP().setReadAccess(true, objectId: objectId) XCTAssertTrue(clp.hasReadAccess(objectId)) XCTAssertFalse(clp.hasWriteAccess(objectId)) @@ -762,7 +808,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasWriteAccess(objectId)) } - func testCLPReadAccessUserHas() throws { + func testReadAccessUserHas() throws { let clp = try ParseCLP().setReadAccess(true, user: user) XCTAssertTrue(try clp.hasReadAccess(user)) XCTAssertFalse(try clp.hasWriteAccess(user)) @@ -776,7 +822,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(try clp3.hasWriteAccess(user)) } - func testCLPReadAccessPointerHas() throws { + func testReadAccessPointerHas() throws { let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) XCTAssertTrue(clp.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp.hasWriteAccess(try user.toPointer())) @@ -790,7 +836,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) } - func testCLPReadAccessRoleHas() throws { + func testReadAccessRoleHas() throws { let name = "hello" let role = try Role(name: name) let clp = try ParseCLP().setReadAccess(true, role: role) From 77eb99b429624f786d285b3a2569266b171b63d9 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 11:12:25 -0400 Subject: [PATCH 42/55] finish CLP access tests --- Sources/ParseSwift/Types/ParseCLP.swift | 4 ++-- Tests/ParseSwiftTests/ParseCLPTests.swift | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index b513ebf2b..bd3c3f8b1 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -191,7 +191,7 @@ public extension ParseCLP { get/find/count/create/update/delete/addField. - returns: **true** if access is allowed, **false** otherwise. */ - func hasAccessPublic(_ action: Action) throws -> Bool { + func hasAccessPublic(_ action: Action) -> Bool { hasAccess(action.keyPath(), for: Access.publicScope.rawValue) } @@ -202,7 +202,7 @@ public extension ParseCLP { - returns: **true** if access is allowed, **false** otherwise. - warning: Requires Parse Server 2.3.0+. */ - func hasAccessRequiresAuthentication(_ action: Action) throws -> Bool { + func hasAccessRequiresAuthentication(_ action: Action) -> Bool { hasAccess(action.keyPath(), for: Access.requiresAuthentication.rawValue) } diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 12758777c..8bf9854e1 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -138,6 +138,22 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } + func testPublicAccess() throws { + let clp = ParseCLP().setAccessPublic(true, on: .create) + XCTAssertTrue(clp.hasAccessPublic(.create)) + + let clp2 = clp.setAccessPublic(false, on: .create) + XCTAssertFalse(clp2.hasAccessPublic(.create)) + } + + func testRequiresAuthenticationAccess() throws { + let clp = ParseCLP().setAccessRequiresAuthentication(true, on: .create) + XCTAssertTrue(clp.hasAccessRequiresAuthentication(.create)) + + let clp2 = clp.setAccessRequiresAuthentication(false, on: .create) + XCTAssertFalse(clp2.hasAccessRequiresAuthentication(.create)) + } + func testAccessUser() throws { let clp = try ParseCLP().setAccess(true, on: .create, for: user) XCTAssertTrue(try clp.hasAccess(.create, for: user)) From 3516ef637506feb86679a611e674b58433bce13f Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 11:31:10 -0400 Subject: [PATCH 43/55] remove unnecessary code --- Sources/ParseSwift/Types/ParseCLP.swift | 145 +++--------------------- 1 file changed, 15 insertions(+), 130 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index bd3c3f8b1..a43b15c53 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -18,12 +18,21 @@ public struct ParseCLP: Codable, Equatable { var update: [String: AnyCodable]? var delete: [String: AnyCodable]? var addField: [String: AnyCodable]? - /// The users, roles, and access level restrictions who cannot access particular fields in a Parse class. - public internal(set) var protectedFields: [String: Set]? - /// The users and roles that can perform get/count/find actions on a Parse class. - public internal(set) var readUserFields: Set? - /// The users and roles that can perform create/delete/update/addField actions on a Parse class. - public internal(set) var writeUserFields: Set? + /** + The users, roles, and access level restrictions who cannot access particular + fields in a Parse class. + */ + public var protectedFields: [String: Set]? + /** + The fields that contain either a `ParseUser` or an array of `ParseUser`'s that + can perform get/count/find actions on a Parse class. + */ + public var readUserFields: Set? + /** + The fields that contain either a `ParseUser` or an array of `ParseUser`'s that can perform + create/delete/update/addField actions on a Parse class. + */ + public var writeUserFields: Set? /// The avialable actions to perform on a Parse class. public enum Action { @@ -1017,130 +1026,6 @@ public extension ParseCLP { } } -// MARK: UserFields -extension ParseCLP { - func getUser(_ keyPath: KeyPath?>) -> Set { - self[keyPath: keyPath] ?? [] - } - - func setUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - var mutableCLP = self - mutableCLP[keyPath: keyPath] = fields - return mutableCLP - } - - func addUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - if let currentSet = self[keyPath: keyPath] { - var mutableCLP = self - mutableCLP[keyPath: keyPath] = currentSet.union(currentSet) - return mutableCLP - } else { - return setUser(keyPath, fields: fields) - } - } - - func removeUser(_ keyPath: WritableKeyPath?>, - fields: Set) -> Self { - var mutableCLP = self - fields.forEach { - mutableCLP[keyPath: keyPath]?.remove($0) - } - return mutableCLP - } -} - -// MARK: WriteUserFields -public extension ParseCLP { - - /** - Get the set of `ParseUser` and array `ParseUser` fields that can - perform create/update/delete/addField actions on this Parse class. - - returns: The set of `ParseUser` and array `ParseUser` fields. - */ - func getWriteUserFields() -> Set { - getUser(\.writeUserFields) - } - - /** - Set the `ParseUser` and array `ParseUser` fields that can - perform create/update/delete/addField actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setWriteUser(_ fields: Set) -> Self { - setUser(\.writeUserFields, fields: fields) - } - - /** - Add to the set of `ParseUser` and array `ParseUser` fields that can - perform create/update/delete/addField actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method adds on to the current set of `fields` in the CLP. - */ - func addWriteUser(_ fields: Set) -> Self { - addUser(\.writeUserFields, fields: fields) - } - - /** - Remove fields from the set of `ParseUser` and array `ParseUser` fields that can - perform create/update/delete/addField actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method removes from the current set of `fields` in the CLP. - */ - func removeWriteUser(_ fields: Set) -> Self { - removeUser(\.writeUserFields, fields: fields) - } -} - -// MARK: ReadUserFields -public extension ParseCLP { - - /** - Get the set of `ParseUser` and array `ParseUser` fields that can - perform get/find/count actions on this Parse class. - - returns: The set of `ParseUser` and array `ParseUser` fields. - */ - func getReadUserFields() -> Set { - getUser(\.readUserFields) - } - - /** - Set the `ParseUser` and array `ParseUser` fields that can - perform get/find/count actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - */ - func setReadUser(_ fields: Set) -> Self { - setUser(\.readUserFields, fields: fields) - } - - /** - Add to the set of `ParseUser` and array `ParseUser` fields that can - perform get/find/count actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method adds on to the current set of `fields` in the CLP. - */ - func addReadUser(_ fields: Set) -> Self { - addUser(\.readUserFields, fields: fields) - } - - /** - Remove fields from the set of `ParseUser` and array `ParseUser` fields that can - perform get/find/count actions on this Parse class. - - parameter fields: The set of `ParseUser` and array `ParseUser` fields. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method removes from the current set of `fields` in the CLP. - */ - func removeReadUser(_ fields: Set) -> Self { - removeUser(\.readUserFields, fields: fields) - } -} - // MARK: CustomDebugStringConvertible extension ParseCLP: CustomDebugStringConvertible { public var debugDescription: String { From 86858b2f6dabd0671f8ca934e3f0a64535ed9200 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 11:51:18 -0400 Subject: [PATCH 44/55] organize ParseCLP --- Sources/ParseSwift/Types/ParseCLP.swift | 167 ++++++++++++------------ 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index a43b15c53..d8dc43650 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -102,11 +102,26 @@ public struct ParseCLP: Codable, Equatable { /// Creates an empty instance of CLP. public init() { } - - func getPointerFields(_ keyPath: KeyPath) -> Set { - self[keyPath: keyPath]?[Access.pointerFields.rawValue]?.value as? Set ?? [] + + /** + Creates an instance of CLP with particular access. + - parameter requiresAuthentication: Read/Write to a Parse class requires users to be authenticated. + - parameter publicAccess:Read/Write to a Parse class can be done by the public. + - warning: Setting `requiresAuthentication` and `publicAccess` does not give **addField** + access. You can set **addField** access after creating an instance of CLP. + - warning: Requires Parse Server 2.3.0+. + */ + public init(requiresAuthentication: Bool, publicAccess: Bool) { + let clp = setWriteAccessRequiresAuthentication(requiresAuthentication) + .setReadAccessRequiresAuthentication(requiresAuthentication) + .setWriteAccessPublic(publicAccess) + .setReadAccessPublic(publicAccess) + self = clp } +} +// MARK: Default Implementation +extension ParseCLP { func hasAccess(_ keyPath: KeyPath, for entity: String) -> Bool { self[keyPath: keyPath]?[entity]?.value as? Bool ?? false @@ -130,6 +145,10 @@ public struct ParseCLP: Codable, Equatable { return mutableCLP } + func getPointerFields(_ keyPath: KeyPath) -> Set { + self[keyPath: keyPath]?[Access.pointerFields.rawValue]?.value as? Set ?? [] + } + func setPointer(_ fields: Set, on keyPath: WritableKeyPath) -> Self { var mutableCLP = self @@ -165,35 +184,8 @@ public struct ParseCLP: Codable, Equatable { } } -// MARK: Default Implementation +// MARK: Standard Access public extension ParseCLP { - - /** - Creates an instance of CLP with particular access. - - parameter requiresAuthentication: Read/Write to a Parse class requires users to be authenticated. - - parameter publicAccess:Read/Write to a Parse class can be done by the public. - - warning: Setting `requiresAuthentication` and `publicAccess` does not give **addField** - access. You can set **addField** access after creating an instance of CLP. - - warning: Requires Parse Server 2.3.0+. - */ - init(requiresAuthentication: Bool, publicAccess: Bool) { - let clp = setWriteAccessRequiresAuthentication(requiresAuthentication) - .setReadAccessRequiresAuthentication(requiresAuthentication) - .setWriteAccessPublic(publicAccess) - .setReadAccessPublic(publicAccess) - self = clp - } - - /** - Retreive the fields in a Parse class that determine access for a specific `Action`. - - parameter action: An enum value of one of the following actions: - get/find/count/create/update/delete/addField. - - returns: The set of user fields given access to a particular `Action`. - */ - func getPointerFields(_ action: Action) throws -> Set { - getPointerFields(action.keyPath()) - } - /** Checks if an `Action` on a Parse class has public access. - parameter action: An enum value of one of the following actions: @@ -351,59 +343,10 @@ public extension ParseCLP { let roleNameAccess = try ParseACL.getRoleAccessName(role) return setAccess(allow, on: action.writableKeyPath(), for: roleNameAccess) } - - /** - Give access to a set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. - - parameter action: An enum value of one of the following actions: - get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to give access to. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method replaces the current set of `fields` in the CLP. - - warning: Requires Parse Server 3.1.1+. - */ - func setPointerFields(_ fields: Set, - on action: Action) -> Self { - setPointer(fields, on: action.writableKeyPath()) - } - - /** - Add access to an additional set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. - - parameter action: An enum value of one of the following actions: - get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to add access to. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method adds on to the current set of `fields` in the CLP. - - warning: Requires Parse Server 3.1.1+. - */ - func addPointerFields(_ fields: Set, - on action: Action) -> Self { - addPointer(fields, on: action.writableKeyPath()) - } - - /** - Remove access for the set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. - - parameter action: An enum value of one of the following actions: - get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to remove access to. - - returns: A mutated instance of `ParseCLP` for easy chaining. - - note: This method removes from the current set of `fields` in the CLP. - - warning: Requires Parse Server 3.1.1+. - */ - func removePointerFields(_ fields: Set, - on action: Action) -> Self { - removePointer(fields, on: action.writableKeyPath()) - } } // MARK: WriteAccess public extension ParseCLP { - internal func hasWriteAccess(_ entity: String, check addField: Bool) -> Bool { let access = hasAccess(.create, for: entity) @@ -596,7 +539,6 @@ public extension ParseCLP { // MARK: ReadAccess public extension ParseCLP { - /** Check whether user authentication is required to perform get/find/count actions on a Parse class. - returns: **true** if has access, **false** otherwise. @@ -738,7 +680,68 @@ public extension ParseCLP { } } -// MARK: Protected +// MARK: Pointer Access +public extension ParseCLP { + /** + Retreive the fields in a Parse class that determine access for a specific `Action`. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - returns: The set of user fields given access to a particular `Action`. + */ + func getPointerFields(_ action: Action) throws -> Set { + getPointerFields(action.keyPath()) + } + + /** + Give access to a set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to give access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method replaces the current set of `fields` in the CLP. + - warning: Requires Parse Server 3.1.1+. + */ + func setPointerFields(_ fields: Set, + on action: Action) -> Self { + setPointer(fields, on: action.writableKeyPath()) + } + + /** + Add access to an additional set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to add access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method adds on to the current set of `fields` in the CLP. + - warning: Requires Parse Server 3.1.1+. + */ + func addPointerFields(_ fields: Set, + on action: Action) -> Self { + addPointer(fields, on: action.writableKeyPath()) + } + + /** + Remove access for the set of `ParseUser` column's or array `ParseUser` + column's for a specific `Action` on a Parse class. + - parameter action: An enum value of one of the following actions: + get/find/count/create/update/delete/addField. + - parameter fields: The set of `ParseUser` columns or array of `ParseUser` + columns to remove access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - note: This method removes from the current set of `fields` in the CLP. + - warning: Requires Parse Server 3.1.1+. + */ + func removePointerFields(_ fields: Set, + on action: Action) -> Self { + removePointer(fields, on: action.writableKeyPath()) + } +} + +// MARK: Protected Fields public extension ParseCLP { internal func getProtected(_ keyPath: KeyPath]?>, for entity: String) -> Set { From e05854f9089d5a24ca39b9304a99754719bfa8c7 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 11:54:23 -0400 Subject: [PATCH 45/55] nit --- ParseSwift.playground/Sources/Common.swift | 4 ++-- Sources/ParseSwift/Types/ParseCLP.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index fb32c0785..214f8c327 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "http://localhost:1337/1")!, + serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index d8dc43650..5351b8072 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -102,7 +102,7 @@ public struct ParseCLP: Codable, Equatable { /// Creates an empty instance of CLP. public init() { } - + /** Creates an instance of CLP with particular access. - parameter requiresAuthentication: Read/Write to a Parse class requires users to be authenticated. From b1e2c434e5c0bb5ebb90e676a0b93aeb68a17ca0 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 11:56:18 -0400 Subject: [PATCH 46/55] nit --- ParseSwift.playground/Sources/Common.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index 214f8c327..fb32c0785 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -5,7 +5,7 @@ public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", masterKey: "masterKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, usingTransactions: false, usingEqualQueryConstraint: false) } @@ -13,7 +13,7 @@ public func initializeParse() { public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", clientKey: "clientKey", - serverURL: URL(string: "https://parse-swift.herokuapp.com/1")!, + serverURL: URL(string: "http://localhost:1337/1")!, allowingCustomObjectIds: true, usingEqualQueryConstraint: false) } From f20b45ca6b1055b34bce639e172a177cd8a6c125 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 12:50:05 -0400 Subject: [PATCH 47/55] set protectedFields internal set --- Sources/ParseSwift/Types/ParseCLP.swift | 74 +++++++------ Tests/ParseSwiftTests/ParseCLPTests.swift | 128 +++++++++++----------- 2 files changed, 102 insertions(+), 100 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 5351b8072..836a31aaf 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -22,15 +22,15 @@ public struct ParseCLP: Codable, Equatable { The users, roles, and access level restrictions who cannot access particular fields in a Parse class. */ - public var protectedFields: [String: Set]? + public internal(set) var protectedFields: [String: Set]? /** - The fields that contain either a `ParseUser` or an array of `ParseUser`'s that + Fields of `ParseUser` type or an array of `ParseUser`'s that can perform get/count/find actions on a Parse class. */ public var readUserFields: Set? /** - The fields that contain either a `ParseUser` or an array of `ParseUser`'s that can perform - create/delete/update/addField actions on a Parse class. + Fields of `ParseUser` type or an array of `ParseUser`'s that + can perform create/delete/update/addField actions on a Parse class. */ public var writeUserFields: Set? @@ -109,7 +109,7 @@ public struct ParseCLP: Codable, Equatable { - parameter publicAccess:Read/Write to a Parse class can be done by the public. - warning: Setting `requiresAuthentication` and `publicAccess` does not give **addField** access. You can set **addField** access after creating an instance of CLP. - - warning: Requires Parse Server 2.3.0+. + - warning: Use of `requiresAuthentication == true` requires Parse Server 2.3.0+. */ public init(requiresAuthentication: Bool, publicAccess: Bool) { let clp = setWriteAccessRequiresAuthentication(requiresAuthentication) @@ -450,7 +450,7 @@ public extension ParseCLP { */ func setWriteAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setWriteAccess(allow, objectId: Access.requiresAuthentication.rawValue, canAddField: addField) + setWriteAccess(allow, for: Access.requiresAuthentication.rawValue, canAddField: addField) } /** @@ -462,7 +462,7 @@ public extension ParseCLP { */ func setWriteAccessPublic(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setWriteAccess(allow, objectId: Access.publicScope.rawValue, canAddField: addField) + setWriteAccess(allow, for: Access.publicScope.rawValue, canAddField: addField) } /** @@ -475,7 +475,7 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteAccess(_ allow: Bool, - objectId: String, + for objectId: String, canAddField addField: Bool = false) -> Self { var updatedCLP = self .setAccess(allow, on: .create, for: objectId) @@ -499,10 +499,10 @@ public extension ParseCLP { - throws: An error of type `ParseError`. */ func setWriteAccess(_ allow: Bool, - user: U, + for user: U, canAddField addField: Bool = false) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setWriteAccess(allow, objectId: objectId, canAddField: addField) + return setWriteAccess(allow, for: objectId, canAddField: addField) } /** @@ -515,9 +515,9 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setWriteAccess(_ allow: Bool, - user: Pointer, + for user: Pointer, canAddField addField: Bool = false) -> Self where U: ParseUser { - setWriteAccess(allow, objectId: user.objectId, canAddField: addField) + setWriteAccess(allow, for: user.objectId, canAddField: addField) } /** @@ -530,10 +530,10 @@ public extension ParseCLP { - throws: An error of type `ParseError`. */ func setWriteAccess(_ allow: Bool, - role: R, + for role: R, canAddField addField: Bool = false) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setWriteAccess(allow, objectId: roleNameAccess, canAddField: addField) + return setWriteAccess(allow, for: roleNameAccess, canAddField: addField) } } @@ -616,7 +616,7 @@ public extension ParseCLP { */ func setReadAccessRequiresAuthentication(_ allow: Bool, canAddField addField: Bool = false) -> Self { - setReadAccess(allow, objectId: Access.requiresAuthentication.rawValue) + setReadAccess(allow, for: Access.requiresAuthentication.rawValue) } /** @@ -625,7 +625,7 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadAccessPublic(_ allow: Bool) -> Self { - setReadAccess(allow, objectId: Access.publicScope.rawValue) + setReadAccess(allow, for: Access.publicScope.rawValue) } /** @@ -635,7 +635,7 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadAccess(_ allow: Bool, - objectId: String) -> Self { + for objectId: String) -> Self { let updatedCLP = self .setAccess(allow, on: .get, for: objectId) .setAccess(allow, on: .find, for: objectId) @@ -651,9 +651,9 @@ public extension ParseCLP { - throws: An error of type `ParseError`. */ func setReadAccess(_ allow: Bool, - user: U) throws -> Self where U: ParseUser { + for user: U) throws -> Self where U: ParseUser { let objectId = try user.toPointer().objectId - return setReadAccess(allow, objectId: objectId) + return setReadAccess(allow, for: objectId) } /** @@ -663,8 +663,8 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. */ func setReadAccess(_ allow: Bool, - user: Pointer) -> Self where U: ParseUser { - return setReadAccess(allow, objectId: user.objectId) + for user: Pointer) -> Self where U: ParseUser { + return setReadAccess(allow, for: user.objectId) } /** @@ -674,9 +674,10 @@ public extension ParseCLP { - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ - func setReadAccess(_ allow: Bool, role: R) throws -> Self where R: ParseRole { + func setReadAccess(_ allow: Bool, + for role: R) throws -> Self where R: ParseRole { let roleNameAccess = try ParseACL.getRoleAccessName(role) - return setReadAccess(allow, objectId: roleNameAccess) + return setReadAccess(allow, for: roleNameAccess) } } @@ -686,19 +687,20 @@ public extension ParseCLP { Retreive the fields in a Parse class that determine access for a specific `Action`. - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - - returns: The set of user fields given access to a particular `Action`. + - returns: The set of fields that are either of`ParseUser` type or + an array of `ParseUser`'s. */ func getPointerFields(_ action: Action) throws -> Set { getPointerFields(action.keyPath()) } /** - Give access to a set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. + Give access to a set of fields that are either of `ParseUser` type or an array + `ParseUser`'s for a specific `Action` on a Parse class. - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to give access to. + - parameter fields: The set of fields that are either of`ParseUser` type or + an array of `ParseUser`'s. - returns: A mutated instance of `ParseCLP` for easy chaining. - note: This method replaces the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. @@ -709,12 +711,12 @@ public extension ParseCLP { } /** - Add access to an additional set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. + Add access to an additional set of fields that are either of `ParseUser` type or an array + `ParseUser`'s for a specific `Action` on a Parse class. - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to add access to. + - parameter fields: The set of fields that are either of`ParseUser` type or + an array of `ParseUser`'s. - returns: A mutated instance of `ParseCLP` for easy chaining. - note: This method adds on to the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. @@ -725,12 +727,12 @@ public extension ParseCLP { } /** - Remove access for the set of `ParseUser` column's or array `ParseUser` - column's for a specific `Action` on a Parse class. + Remove access for the set of fields that are either of `ParseUser` type or an array + `ParseUser`'s for a specific `Action` on a Parse class. - parameter action: An enum value of one of the following actions: get/find/count/create/update/delete/addField. - - parameter fields: The set of `ParseUser` columns or array of `ParseUser` - columns to remove access to. + - parameter fields: The set of fields that are either of`ParseUser` type or + an array of `ParseUser`'s. - returns: A mutated instance of `ParseCLP` for easy chaining. - note: This method removes from the current set of `fields` in the CLP. - warning: Requires Parse Server 3.1.1+. diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 8bf9854e1..408dccec9 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -269,7 +269,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testWriteAccessObjectIdSet() throws { - let clp = ParseCLP().setWriteAccess(true, objectId: objectId) + let clp = ParseCLP().setWriteAccess(true, for: objectId) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) XCTAssertEqual(clp.create?[objectId], true) @@ -278,7 +278,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.count?[objectId]) XCTAssertNil(clp.addField?[objectId]) - let clp2 = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + let clp2 = ParseCLP().setWriteAccess(true, for: objectId, canAddField: true) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) XCTAssertEqual(clp2.create?[objectId], true) @@ -287,7 +287,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertEqual(clp2.addField?[objectId], true) - let clp3 = clp.setWriteAccess(false, objectId: objectId) + let clp3 = clp.setWriteAccess(false, for: objectId) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -296,7 +296,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.count?[objectId]) XCTAssertNil(clp3.addField?[objectId]) - let clp4 = clp2.setWriteAccess(false, objectId: objectId) + let clp4 = clp2.setWriteAccess(false, for: objectId) XCTAssertNil(clp4.get?[objectId]) XCTAssertNil(clp4.find?[objectId]) XCTAssertNil(clp4.create?[objectId]) @@ -307,13 +307,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testWriteAccessObjectIdSetEncode() throws { - let clp = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + let clp = ParseCLP().setWriteAccess(true, for: objectId, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") } func testWriteAccessUserSet() throws { - let clp = try ParseCLP().setWriteAccess(true, user: user) + let clp = try ParseCLP().setWriteAccess(true, for: user) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) XCTAssertEqual(clp.create?[objectId], true) @@ -322,7 +322,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.count?[objectId]) XCTAssertNil(clp.addField?[objectId]) - let clp2 = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + let clp2 = try ParseCLP().setWriteAccess(true, for: user, canAddField: true) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) XCTAssertEqual(clp2.create?[objectId], true) @@ -331,7 +331,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertEqual(clp2.addField?[objectId], true) - let clp3 = try clp.setWriteAccess(false, user: user) + let clp3 = try clp.setWriteAccess(false, for: user) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -340,7 +340,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.count?[objectId]) XCTAssertNil(clp3.addField?[objectId]) - let clp4 = try clp2.setWriteAccess(false, user: user) + let clp4 = try clp2.setWriteAccess(false, for: user) XCTAssertNil(clp4.get?[objectId]) XCTAssertNil(clp4.find?[objectId]) XCTAssertNil(clp4.create?[objectId]) @@ -351,13 +351,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testWriteAccessUserSetEncode() throws { - let clp = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + let clp = try ParseCLP().setWriteAccess(true, for: user, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") } func testWriteAccessPointerSet() throws { - let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) + let clp = ParseCLP().setWriteAccess(true, for: try user.toPointer()) XCTAssertNil(clp.get?[objectId]) XCTAssertNil(clp.find?[objectId]) XCTAssertEqual(clp.create?[objectId], true) @@ -367,7 +367,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[objectId]) let clp2 = ParseCLP().setWriteAccess(true, - user: try user.toPointer(), + for: try user.toPointer(), canAddField: true) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) @@ -377,7 +377,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertEqual(clp2.addField?[objectId], true) - let clp3 = clp.setWriteAccess(false, user: try user.toPointer()) + let clp3 = clp.setWriteAccess(false, for: try user.toPointer()) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -386,7 +386,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.count?[objectId]) XCTAssertNil(clp3.addField?[objectId]) - let clp4 = clp2.setWriteAccess(false, user: try user.toPointer()) + let clp4 = clp2.setWriteAccess(false, for: try user.toPointer()) XCTAssertNil(clp4.get?[objectId]) XCTAssertNil(clp4.find?[objectId]) XCTAssertNil(clp4.create?[objectId]) @@ -398,7 +398,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length func testWriteAccessPointerSetEncode() throws { let clp = ParseCLP().setWriteAccess(true, - user: try user.toPointer(), + for: try user.toPointer(), canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(objectId)\":true},\"create\":{\"\(objectId)\":true},\"delete\":{\"\(objectId)\":true},\"update\":{\"\(objectId)\":true}})") @@ -408,7 +408,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) - let clp = try ParseCLP().setWriteAccess(true, role: role) + let clp = try ParseCLP().setWriteAccess(true, for: role) XCTAssertNil(clp.get?[roleName]) XCTAssertNil(clp.find?[roleName]) XCTAssertEqual(clp.create?[roleName], true) @@ -418,7 +418,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[roleName]) let clp2 = try ParseCLP().setWriteAccess(true, - role: role, + for: role, canAddField: true) XCTAssertNil(clp2.get?[roleName]) XCTAssertNil(clp2.find?[roleName]) @@ -428,7 +428,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[roleName]) XCTAssertEqual(clp2.addField?[roleName], true) - let clp3 = try clp.setWriteAccess(false, role: role) + let clp3 = try clp.setWriteAccess(false, for: role) XCTAssertNil(clp3.get?[roleName]) XCTAssertNil(clp3.find?[roleName]) XCTAssertNil(clp3.create?[roleName]) @@ -437,7 +437,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp3.count?[roleName]) XCTAssertNil(clp3.addField?[roleName]) - let clp4 = try clp2.setWriteAccess(false, role: role) + let clp4 = try clp2.setWriteAccess(false, for: role) XCTAssertNil(clp4.get?[roleName]) XCTAssertNil(clp4.find?[roleName]) XCTAssertNil(clp4.create?[roleName]) @@ -452,7 +452,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) let clp = try ParseCLP().setWriteAccess(true, - role: role, + for: role, canAddField: true) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"\(roleName)\":true},\"create\":{\"\(roleName)\":true},\"delete\":{\"\(roleName)\":true},\"update\":{\"\(roleName)\":true}})") @@ -487,58 +487,58 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testWriteAccessObjectIdHas() throws { - let clp = ParseCLP().setWriteAccess(true, objectId: objectId) + let clp = ParseCLP().setWriteAccess(true, for: objectId) XCTAssertFalse(clp.hasReadAccess(objectId)) XCTAssertTrue(clp.hasWriteAccess(objectId)) XCTAssertFalse(clp.hasWriteAccess(objectId, check: true)) - let clp2 = ParseCLP().setWriteAccess(false, objectId: objectId) + let clp2 = ParseCLP().setWriteAccess(false, for: objectId) XCTAssertFalse(clp2.hasReadAccess(objectId)) XCTAssertFalse(clp2.hasWriteAccess(objectId)) - let clp3 = clp.setWriteAccess(false, objectId: objectId) + let clp3 = clp.setWriteAccess(false, for: objectId) XCTAssertFalse(clp3.hasReadAccess(objectId)) XCTAssertFalse(clp3.hasWriteAccess(objectId)) - let clp4 = ParseCLP().setWriteAccess(true, objectId: objectId, canAddField: true) + let clp4 = ParseCLP().setWriteAccess(true, for: objectId, canAddField: true) XCTAssertFalse(clp4.hasReadAccess(objectId)) XCTAssertTrue(clp4.hasWriteAccess(objectId, check: true)) } func testWriteAccessUserHas() throws { - let clp = try ParseCLP().setWriteAccess(true, user: user) + let clp = try ParseCLP().setWriteAccess(true, for: user) XCTAssertFalse(try clp.hasReadAccess(user)) XCTAssertTrue(try clp.hasWriteAccess(user)) XCTAssertFalse(try clp.hasWriteAccess(user, checkAddField: true)) - let clp2 = try ParseCLP().setWriteAccess(false, user: user) + let clp2 = try ParseCLP().setWriteAccess(false, for: user) XCTAssertFalse(try clp2.hasReadAccess(user)) XCTAssertFalse(try clp2.hasWriteAccess(user)) - let clp3 = try clp.setWriteAccess(false, user: user) + let clp3 = try clp.setWriteAccess(false, for: user) XCTAssertFalse(try clp3.hasReadAccess(user)) XCTAssertFalse(try clp3.hasWriteAccess(user)) - let clp4 = try ParseCLP().setWriteAccess(true, user: user, canAddField: true) + let clp4 = try ParseCLP().setWriteAccess(true, for: user, canAddField: true) XCTAssertFalse(try clp4.hasReadAccess(user)) XCTAssertTrue(try clp4.hasWriteAccess(user, checkAddField: true)) } func testWriteAccessPointerHas() throws { - let clp = ParseCLP().setWriteAccess(true, user: try user.toPointer()) + let clp = ParseCLP().setWriteAccess(true, for: try user.toPointer()) XCTAssertFalse(clp.hasReadAccess(try user.toPointer())) XCTAssertTrue(clp.hasWriteAccess(try user.toPointer())) XCTAssertFalse(clp.hasWriteAccess(try user.toPointer(), checkAddField: true)) - let clp2 = ParseCLP().setWriteAccess(false, user: try user.toPointer()) + let clp2 = ParseCLP().setWriteAccess(false, for: try user.toPointer()) XCTAssertFalse(clp2.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp2.hasWriteAccess(try user.toPointer())) - let clp3 = clp.setWriteAccess(false, user: try user.toPointer()) + let clp3 = clp.setWriteAccess(false, for: try user.toPointer()) XCTAssertFalse(clp3.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) - let clp4 = ParseCLP().setWriteAccess(true, user: try user.toPointer(), canAddField: true) + let clp4 = ParseCLP().setWriteAccess(true, for: try user.toPointer(), canAddField: true) XCTAssertFalse(clp4.hasReadAccess(try user.toPointer())) XCTAssertTrue(clp4.hasWriteAccess(try user.toPointer(), checkAddField: true)) } @@ -546,20 +546,20 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length func testWriteAccessRoleHas() throws { let name = "hello" let role = try Role(name: name) - let clp = try ParseCLP().setWriteAccess(true, role: role) + let clp = try ParseCLP().setWriteAccess(true, for: role) XCTAssertFalse(try clp.hasReadAccess(role)) XCTAssertTrue(try clp.hasWriteAccess(role)) XCTAssertFalse(try clp.hasWriteAccess(role, checkAddField: true)) - let clp2 = try ParseCLP().setWriteAccess(false, role: role) + let clp2 = try ParseCLP().setWriteAccess(false, for: role) XCTAssertFalse(try clp2.hasReadAccess(role)) XCTAssertFalse(try clp2.hasWriteAccess(role)) - let clp3 = try clp.setWriteAccess(false, role: role) + let clp3 = try clp.setWriteAccess(false, for: role) XCTAssertFalse(try clp3.hasReadAccess(role)) XCTAssertFalse(try clp3.hasWriteAccess(role)) - let clp4 = try ParseCLP().setWriteAccess(true, role: role, canAddField: true) + let clp4 = try ParseCLP().setWriteAccess(true, for: role, canAddField: true) XCTAssertFalse(try clp4.hasReadAccess(role)) XCTAssertTrue(try clp4.hasWriteAccess(role, checkAddField: true)) } @@ -635,7 +635,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testReadAccessObjectIdSet() throws { - let clp = ParseCLP().setReadAccess(true, objectId: objectId) + let clp = ParseCLP().setReadAccess(true, for: objectId) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) XCTAssertNil(clp.create?[objectId]) @@ -644,7 +644,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.count?[objectId], true) XCTAssertNil(clp.addField?[objectId]) - let clp2 = ParseCLP().setReadAccess(false, objectId: objectId) + let clp2 = ParseCLP().setReadAccess(false, for: objectId) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) XCTAssertNil(clp2.create?[objectId]) @@ -653,7 +653,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertNil(clp2.addField?[objectId]) - let clp3 = clp.setReadAccess(false, objectId: objectId) + let clp3 = clp.setReadAccess(false, for: objectId) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -664,13 +664,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testReadAccessObjectIdSetEncode() throws { - let clp = ParseCLP().setReadAccess(true, objectId: objectId) + let clp = ParseCLP().setReadAccess(true, for: objectId) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } func testReadAccessUserSet() throws { - let clp = try ParseCLP().setReadAccess(true, user: user) + let clp = try ParseCLP().setReadAccess(true, for: user) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) XCTAssertNil(clp.create?[objectId]) @@ -679,7 +679,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.count?[objectId], true) XCTAssertNil(clp.addField?[objectId]) - let clp2 = try ParseCLP().setReadAccess(false, user: user) + let clp2 = try ParseCLP().setReadAccess(false, for: user) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) XCTAssertNil(clp2.create?[objectId]) @@ -688,7 +688,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertNil(clp2.addField?[objectId]) - let clp3 = try clp.setReadAccess(false, user: user) + let clp3 = try clp.setReadAccess(false, for: user) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -699,13 +699,13 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testReadAccessUserSetEncode() throws { - let clp = try ParseCLP().setReadAccess(true, user: user) + let clp = try ParseCLP().setReadAccess(true, for: user) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } func testReadAccessPointerSet() throws { - let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) + let clp = ParseCLP().setReadAccess(true, for: try user.toPointer()) XCTAssertEqual(clp.get?[objectId], true) XCTAssertEqual(clp.find?[objectId], true) XCTAssertNil(clp.create?[objectId]) @@ -714,7 +714,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.count?[objectId], true) XCTAssertNil(clp.addField?[objectId]) - let clp2 = ParseCLP().setReadAccess(false, user: try user.toPointer()) + let clp2 = ParseCLP().setReadAccess(false, for: try user.toPointer()) XCTAssertNil(clp2.get?[objectId]) XCTAssertNil(clp2.find?[objectId]) XCTAssertNil(clp2.create?[objectId]) @@ -723,7 +723,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[objectId]) XCTAssertNil(clp2.addField?[objectId]) - let clp3 = clp.setReadAccess(false, user: try user.toPointer()) + let clp3 = clp.setReadAccess(false, for: try user.toPointer()) XCTAssertNil(clp3.get?[objectId]) XCTAssertNil(clp3.find?[objectId]) XCTAssertNil(clp3.create?[objectId]) @@ -735,7 +735,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length func testReadAccessPointerSetEncode() throws { let clp = ParseCLP().setReadAccess(true, - user: try user.toPointer()) + for: try user.toPointer()) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(objectId)\":true},\"find\":{\"\(objectId)\":true},\"get\":{\"\(objectId)\":true}})") } @@ -744,7 +744,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let name = "hello" let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) - let clp = try ParseCLP().setReadAccess(true, role: role) + let clp = try ParseCLP().setReadAccess(true, for: role) XCTAssertEqual(clp.get?[roleName], true) XCTAssertEqual(clp.find?[roleName], true) XCTAssertNil(clp.create?[roleName]) @@ -753,7 +753,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp.count?[roleName], true) XCTAssertNil(clp.addField?[roleName]) - let clp2 = try ParseCLP().setReadAccess(false, role: role) + let clp2 = try ParseCLP().setReadAccess(false, for: role) XCTAssertNil(clp2.get?[roleName]) XCTAssertNil(clp2.find?[roleName]) XCTAssertNil(clp2.create?[roleName]) @@ -762,7 +762,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp2.count?[roleName]) XCTAssertNil(clp2.addField?[roleName]) - let clp3 = try clp.setReadAccess(false, role: role) + let clp3 = try clp.setReadAccess(false, for: role) XCTAssertNil(clp3.get?[roleName]) XCTAssertNil(clp3.find?[roleName]) XCTAssertNil(clp3.create?[roleName]) @@ -777,7 +777,7 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let role = try Role(name: name) let roleName = try ParseACL.getRoleAccessName(role) let clp = try ParseCLP().setReadAccess(true, - role: role) + for: role) // swiftlint:disable:next line_length XCTAssertEqual(clp.description, "ParseCLP ({\"count\":{\"\(roleName)\":true},\"find\":{\"\(roleName)\":true},\"get\":{\"\(roleName)\":true}})") } @@ -811,43 +811,43 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length } func testReadAccessObjectIdHas() throws { - let clp = ParseCLP().setReadAccess(true, objectId: objectId) + let clp = ParseCLP().setReadAccess(true, for: objectId) XCTAssertTrue(clp.hasReadAccess(objectId)) XCTAssertFalse(clp.hasWriteAccess(objectId)) - let clp2 = ParseCLP().setReadAccess(false, objectId: objectId) + let clp2 = ParseCLP().setReadAccess(false, for: objectId) XCTAssertFalse(clp2.hasReadAccess(objectId)) XCTAssertFalse(clp2.hasWriteAccess(objectId)) - let clp3 = clp.setReadAccess(false, objectId: objectId) + let clp3 = clp.setReadAccess(false, for: objectId) XCTAssertFalse(clp3.hasReadAccess(objectId)) XCTAssertFalse(clp3.hasWriteAccess(objectId)) } func testReadAccessUserHas() throws { - let clp = try ParseCLP().setReadAccess(true, user: user) + let clp = try ParseCLP().setReadAccess(true, for: user) XCTAssertTrue(try clp.hasReadAccess(user)) XCTAssertFalse(try clp.hasWriteAccess(user)) - let clp2 = try ParseCLP().setReadAccess(false, user: user) + let clp2 = try ParseCLP().setReadAccess(false, for: user) XCTAssertFalse(try clp2.hasReadAccess(user)) XCTAssertFalse(try clp2.hasWriteAccess(user)) - let clp3 = try clp.setReadAccess(false, user: user) + let clp3 = try clp.setReadAccess(false, for: user) XCTAssertFalse(try clp3.hasReadAccess(user)) XCTAssertFalse(try clp3.hasWriteAccess(user)) } func testReadAccessPointerHas() throws { - let clp = ParseCLP().setReadAccess(true, user: try user.toPointer()) + let clp = ParseCLP().setReadAccess(true, for: try user.toPointer()) XCTAssertTrue(clp.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp.hasWriteAccess(try user.toPointer())) - let clp2 = ParseCLP().setReadAccess(false, user: try user.toPointer()) + let clp2 = ParseCLP().setReadAccess(false, for: try user.toPointer()) XCTAssertFalse(clp2.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp2.hasWriteAccess(try user.toPointer())) - let clp3 = clp.setReadAccess(false, user: try user.toPointer()) + let clp3 = clp.setReadAccess(false, for: try user.toPointer()) XCTAssertFalse(clp3.hasReadAccess(try user.toPointer())) XCTAssertFalse(clp3.hasWriteAccess(try user.toPointer())) } @@ -855,15 +855,15 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length func testReadAccessRoleHas() throws { let name = "hello" let role = try Role(name: name) - let clp = try ParseCLP().setReadAccess(true, role: role) + let clp = try ParseCLP().setReadAccess(true, for: role) XCTAssertTrue(try clp.hasReadAccess(role)) XCTAssertFalse(try clp.hasWriteAccess(role)) - let clp2 = try ParseCLP().setReadAccess(false, role: role) + let clp2 = try ParseCLP().setReadAccess(false, for: role) XCTAssertFalse(try clp2.hasReadAccess(role)) XCTAssertFalse(try clp2.hasWriteAccess(role)) - let clp3 = try clp.setReadAccess(false, role: role) + let clp3 = try clp.setReadAccess(false, for: role) XCTAssertFalse(try clp3.hasReadAccess(role)) XCTAssertFalse(try clp3.hasWriteAccess(role)) } From 70c570e6799252e3c739d5b3ca3ae31bbfb5f4b9 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 14:16:15 -0400 Subject: [PATCH 48/55] finish CLP tests --- Sources/ParseSwift/Types/ParseCLP.swift | 32 ++++- Tests/ParseSwiftTests/ParseCLPTests.swift | 141 ++++++++++++++++++++++ 2 files changed, 169 insertions(+), 4 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 836a31aaf..1dc84bf4a 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -690,7 +690,7 @@ public extension ParseCLP { - returns: The set of fields that are either of`ParseUser` type or an array of `ParseUser`'s. */ - func getPointerFields(_ action: Action) throws -> Set { + func getPointerFields(_ action: Action) -> Set { getPointerFields(action.keyPath()) } @@ -797,7 +797,7 @@ public extension ParseCLP { - returns: The set protected fields that cannot be accessed. - warning: Requires Parse Server 2.3.0+. */ - func getProtectedFieldsRequireAuthentication() -> Set { + func getProtectedFieldsRequiresAuthentication() -> Set { getProtectedFields(Access.requiresAuthentication.rawValue) } @@ -858,7 +858,7 @@ public extension ParseCLP { - throws: An error of type `ParseError`. - warning: Requires Parse Server 2.3.0+. */ - func setProtectedFieldsRequireAuthentication(_ fields: Set) -> Self { + func setProtectedFieldsRequiresAuthentication(_ fields: Set) -> Self { setProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) } @@ -926,7 +926,7 @@ public extension ParseCLP { - note: This method adds on to the current set of `fields` in the CLP. - warning: Requires Parse Server 2.3.0+. */ - func addProtectedFieldsRequireAuthentication(_ fields: Set) -> Self { + func addProtectedFieldsRequiresAuthentication(_ fields: Set) -> Self { addProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) } @@ -979,6 +979,30 @@ public extension ParseCLP { return addProtectedFields(fields, for: roleNameAccess) } + /** + Remove the set of specific fields the public should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter objectId: The `ParseUser` objectId to restrict access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. + */ + func removeProtectedFieldsPublic(_ fields: Set) -> Self { + removeProtected(fields, on: \.protectedFields, for: Access.publicScope.rawValue) + } + + /** + Remove the set of specific fields authenticated users should not have access to + on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. + */ + func removeProtectedFieldsRequiresAuthentication(_ fields: Set) -> Self { + removeProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) + } + /** Remove fields from the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 408dccec9..187008251 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -138,6 +138,142 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(clp.addField?[ParseCLP.Access.requiresAuthentication.rawValue]) } + func testPointerFields() throws { + let fields = Set(["hello", "world"]) + let clp = ParseCLP().setPointerFields(fields, on: .create) + XCTAssertEqual(clp.getPointerFields(.create), fields) + + let newField = Set(["new"]) + let clp2 = clp.addPointerFields(newField, on: .create) + XCTAssertEqual(clp2.getPointerFields(.create), fields.union(newField)) + + let clp3 = clp2.removePointerFields(newField, on: .create) + XCTAssertEqual(clp3.getPointerFields(.create), fields) + + let clp4 = ParseCLP().addPointerFields(newField, on: .create) + XCTAssertEqual(clp4.getPointerFields(.create), newField) + } + + func testPointerFieldsEncode() throws { + let fields = Set(["world"]) + let clp = ParseCLP().setPointerFields(fields, on: .create) + XCTAssertEqual(clp.description, "ParseCLP ({\"create\":{\"pointerFields\":[\"world\"]}})") + } + + func testPointerAndWriteAccessPublicSetEncode() throws { + let fields = Set(["world"]) + let clp = ParseCLP() + .setPointerFields(fields, on: .create) + .setWriteAccessPublic(true, canAddField: true) + // swiftlint:disable:next line_length + XCTAssertEqual(clp.description, "ParseCLP ({\"addField\":{\"*\":true},\"create\":{\"*\":true,\"pointerFields\":[\"world\"]},\"delete\":{\"*\":true},\"update\":{\"*\":true}})") + } + + func testProtectedFieldsPublic() throws { + let fields = Set(["hello", "world"]) + let clp = ParseCLP().setProtectedFieldsPublic(fields) + XCTAssertEqual(clp.getProtectedFieldsPublic(), fields) + + let newField = Set(["new"]) + let clp2 = clp.addProtectedFieldsPublic(newField) + XCTAssertEqual(clp2.getProtectedFieldsPublic(), fields.union(newField)) + + let clp3 = clp2.removeProtectedFieldsPublic(newField) + XCTAssertEqual(clp3.getProtectedFieldsPublic(), fields) + + let clp4 = ParseCLP().addProtectedFieldsPublic(newField) + XCTAssertEqual(clp4.getProtectedFieldsPublic(), newField) + } + + func testProtectedFieldsRequiresAuthentication() throws { + let fields = Set(["hello", "world"]) + let clp = ParseCLP().setProtectedFieldsRequiresAuthentication(fields) + XCTAssertEqual(clp.getProtectedFieldsRequiresAuthentication(), fields) + + let newField = Set(["new"]) + let clp2 = clp.addProtectedFieldsRequiresAuthentication(newField) + XCTAssertEqual(clp2.getProtectedFieldsRequiresAuthentication(), fields.union(newField)) + + let clp3 = clp2.removeProtectedFieldsRequiresAuthentication(newField) + XCTAssertEqual(clp3.getProtectedFieldsRequiresAuthentication(), fields) + + let clp4 = ParseCLP().addProtectedFieldsRequiresAuthentication(newField) + XCTAssertEqual(clp4.getProtectedFieldsRequiresAuthentication(), newField) + } + + func testProtectedFieldsObjectId() throws { + let fields = Set(["hello", "world"]) + let clp = ParseCLP().setProtectedFields(fields, for: objectId) + XCTAssertEqual(clp.getProtectedFields(objectId), fields) + + let newField = Set(["new"]) + let clp2 = clp.addProtectedFields(newField, for: objectId) + XCTAssertEqual(clp2.getProtectedFields(objectId), fields.union(newField)) + + let clp3 = clp2.removeProtectedFields(newField, for: objectId) + XCTAssertEqual(clp3.getProtectedFields(objectId), fields) + + let clp4 = ParseCLP().addProtectedFields(newField, for: objectId) + XCTAssertEqual(clp4.getProtectedFields(objectId), newField) + } + + func testProtectedFieldsUser() throws { + let fields = Set(["hello", "world"]) + let clp = try ParseCLP().setProtectedFields(fields, for: user) + XCTAssertEqual(try clp.getProtectedFields(user), fields) + + let newField = Set(["new"]) + let clp2 = try clp.addProtectedFields(newField, for: user) + XCTAssertEqual(try clp2.getProtectedFields(user), fields.union(newField)) + + let clp3 = try clp2.removeProtectedFields(newField, for: user) + XCTAssertEqual(try clp3.getProtectedFields(user), fields) + + let clp4 = try ParseCLP().addProtectedFields(newField, for: user) + XCTAssertEqual(try clp4.getProtectedFields(user), newField) + } + + func testProtectedFieldsPointer() throws { + let pointer = try user.toPointer() + let fields = Set(["hello", "world"]) + let clp = ParseCLP().setProtectedFields(fields, for: pointer) + XCTAssertEqual(clp.getProtectedFields(pointer), fields) + + let newField = Set(["new"]) + let clp2 = clp.addProtectedFields(newField, for: pointer) + XCTAssertEqual(clp2.getProtectedFields(pointer), fields.union(newField)) + + let clp3 = clp2.removeProtectedFields(newField, for: pointer) + XCTAssertEqual(clp3.getProtectedFields(pointer), fields) + + let clp4 = ParseCLP().addProtectedFields(newField, for: pointer) + XCTAssertEqual(clp4.getProtectedFields(pointer), newField) + } + + func testProtectedFieldsRole() throws { + let role = try Role(name: "hello") + let fields = Set(["hello", "world"]) + let clp = try ParseCLP().setProtectedFields(fields, for: role) + XCTAssertEqual(try clp.getProtectedFields(role), fields) + + let newField = Set(["new"]) + let clp2 = try clp.addProtectedFields(newField, for: role) + XCTAssertEqual(try clp2.getProtectedFields(role), fields.union(newField)) + + let clp3 = try clp2.removeProtectedFields(newField, for: role) + XCTAssertEqual(try clp3.getProtectedFields(role), fields) + + let clp4 = try ParseCLP().addProtectedFields(newField, for: role) + XCTAssertEqual(try clp4.getProtectedFields(role), newField) + } + + func testProtectedFieldsEncode() throws { + let role = try Role(name: "hello") + let fields = Set(["world"]) + let clp = try ParseCLP().setProtectedFields(fields, for: role) + XCTAssertEqual(clp.description, "ParseCLP ({\"protectedFields\":{\"role:hello\":[\"world\"]}})") + } + func testPublicAccess() throws { let clp = ParseCLP().setAccessPublic(true, on: .create) XCTAssertTrue(clp.hasAccessPublic(.create)) @@ -180,6 +316,11 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertFalse(try clp2.hasAccess(.create, for: user)) } + func testAccessEncode() throws { + let clp = ParseCLP().setAccess(true, on: .create, for: objectId) + XCTAssertEqual(clp.description, "ParseCLP ({\"create\":{\"\(objectId)\":true}})") + } + func testWriteAccessPublicSet() throws { let clp = ParseCLP().setWriteAccessPublic(true) XCTAssertNil(clp.get?[ParseCLP.Access.publicScope.rawValue]) From 04ad43a6ac4f4e8eeca7cabb96a23170a4286af1 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Sun, 29 May 2022 17:41:39 -0400 Subject: [PATCH 49/55] refactor ParseSchema --- .../Contents.swift | 1 - ParseSwift.xcodeproj/project.pbxproj | 34 +++ Sources/ParseSwift/Types/ParseField.swift | 140 +++-------- .../ParseSwift/Types/ParseFieldOptions.swift | 41 ++++ Sources/ParseSwift/Types/ParseSchema.swift | 81 +++---- Tests/ParseSwiftTests/ParseCLPTests.swift | 9 + .../ParseSchemaAsyncTests.swift | 135 +++++++++++ .../ParseSchemaCombineTests.swift | 19 ++ Tests/ParseSwiftTests/ParseSchemaTests.swift | 218 ++++++++++++++++++ 9 files changed, 526 insertions(+), 152 deletions(-) create mode 100644 Sources/ParseSwift/Types/ParseFieldOptions.swift create mode 100644 Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift create mode 100644 Tests/ParseSwiftTests/ParseSchemaCombineTests.swift create mode 100644 Tests/ParseSwiftTests/ParseSchemaTests.swift diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 8b75679ca..599cb15cd 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -100,7 +100,6 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: clp) do { gameScoreSchema = try gameScoreSchema.addField("owner", type: .pointer, - target: User(), options: ParseFieldOptions(required: false, defauleValue: nil)) } catch { print("Can't add field: \(gameScoreSchema)") diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 5d55ad04c..9dd6a6bfd 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -277,6 +277,19 @@ 705025992842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; 7050259A2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; 7050259B2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; }; + 7050259D2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */; }; + 7050259E2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */; }; + 7050259F2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */; }; + 705025A12843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */; }; + 705025A22843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */; }; + 705025A32843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */; }; + 705025A5284407C4008D6624 /* ParseSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A4284407C4008D6624 /* ParseSchemaTests.swift */; }; + 705025A6284407C4008D6624 /* ParseSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A4284407C4008D6624 /* ParseSchemaTests.swift */; }; + 705025A7284407C4008D6624 /* ParseSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A4284407C4008D6624 /* ParseSchemaTests.swift */; }; + 705025A928441C96008D6624 /* ParseFieldOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A828441C96008D6624 /* ParseFieldOptions.swift */; }; + 705025AA28441C96008D6624 /* ParseFieldOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A828441C96008D6624 /* ParseFieldOptions.swift */; }; + 705025AB28441C96008D6624 /* ParseFieldOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A828441C96008D6624 /* ParseFieldOptions.swift */; }; + 705025AC28441C96008D6624 /* ParseFieldOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025A828441C96008D6624 /* ParseFieldOptions.swift */; }; 70510AAC259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; 70510AAD259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; 70510AAE259EE25E00FEA700 /* LiveQuerySocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */; }; @@ -982,6 +995,10 @@ 7045769726BD917500F86F71 /* Query+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+async.swift"; sourceTree = ""; }; 7045769C26BD934000F86F71 /* ParseFile+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseFile+async.swift"; sourceTree = ""; }; 705025982842FD3B008D6624 /* ParseCLPTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLPTests.swift; sourceTree = ""; }; + 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemaAsyncTests.swift; sourceTree = ""; }; + 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemaCombineTests.swift; sourceTree = ""; }; + 705025A4284407C4008D6624 /* ParseSchemaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemaTests.swift; sourceTree = ""; }; + 705025A828441C96008D6624 /* ParseFieldOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFieldOptions.swift; sourceTree = ""; }; 70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveQuerySocket.swift; sourceTree = ""; }; 70572670259033A700F0ADD5 /* ParseFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileManager.swift; sourceTree = ""; }; 705727882593FF8000F0ADD5 /* ParseFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileTests.swift; sourceTree = ""; }; @@ -1338,6 +1355,9 @@ 91BB8FD32690D586005A6BA5 /* ParseQueryViewModelTests.swift */, 70D1BD8625B8C37200A42E7C /* ParseRelationTests.swift */, 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */, + 705025A4284407C4008D6624 /* ParseSchemaTests.swift */, + 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */, + 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */, 70C5504525B40D5200B5DBC2 /* ParseSessionTests.swift */, 917BA4512703F55700F8D747 /* ParseTwitterAsyncTests.swift */, 89899D9E26045998002E2043 /* ParseTwitterCombineTests.swift */, @@ -1718,6 +1738,7 @@ 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */, F97B45BF24D9C6F200F4A88B /* ParseError.swift */, 709A148628396B1C00BF85E5 /* ParseField.swift */, + 705025A828441C96008D6624 /* ParseFieldOptions.swift */, F97B45C124D9C6F200F4A88B /* ParseFile.swift */, 7045769C26BD934000F86F71 /* ParseFile+async.swift */, 7044C19025C4F5B60011F6E7 /* ParseFile+combine.swift */, @@ -2270,6 +2291,7 @@ 91285B1C26990D7F0051B544 /* ParsePolygon.swift in Sources */, 91BB8FCA2690AC99005A6BA5 /* QueryViewModel.swift in Sources */, 7085DD9426CBF3A70033B977 /* Documentation.docc in Sources */, + 705025A928441C96008D6624 /* ParseFieldOptions.swift in Sources */, F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A325A119430052CB31 /* Operations.swift in Sources */, 91BB8FCF2690BA70005A6BA5 /* QueryObservable.swift in Sources */, @@ -2450,6 +2472,7 @@ 7FFF552E2217E72A007C3B4E /* AnyEncodableTests.swift in Sources */, 7044C22025C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */, 7FFF55302217E72A007C3B4E /* AnyDecodableTests.swift in Sources */, + 7050259D2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */, 70C7DC2224D20F190050419B /* ParseObjectBatchTests.swift in Sources */, 7044C1BB25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45A2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, @@ -2458,6 +2481,7 @@ 70F03A5E2780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, 4AA807701F794C31008CD551 /* KeychainStoreTests.swift in Sources */, 7085DDB326D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */, + 705025A12843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */, 7044C1F925C5CFAB0011F6E7 /* ParseFileCombineTests.swift in Sources */, 70C5502225B3D8F700B5DBC2 /* ParseAppleTests.swift in Sources */, 917BA4322703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */, @@ -2477,6 +2501,7 @@ 91BB8FD42690D586005A6BA5 /* ParseQueryViewModelTests.swift in Sources */, 911DB13324C494390027F3C7 /* MockURLProtocol.swift in Sources */, 917BA44A2703F10400F8D747 /* ParseAppleAsyncTests.swift in Sources */, + 705025A5284407C4008D6624 /* ParseSchemaTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2512,6 +2537,7 @@ 91285B1D26990D7F0051B544 /* ParsePolygon.swift in Sources */, 91BB8FCB2690AC99005A6BA5 /* QueryViewModel.swift in Sources */, 7085DD9526CBF3A70033B977 /* Documentation.docc in Sources */, + 705025AA28441C96008D6624 /* ParseFieldOptions.swift in Sources */, F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A425A119430052CB31 /* Operations.swift in Sources */, 91BB8FD02690BA70005A6BA5 /* QueryObservable.swift in Sources */, @@ -2701,6 +2727,7 @@ 709B98592556ECAA00507778 /* MockURLResponse.swift in Sources */, 7044C22225C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */, 709B98522556ECAA00507778 /* ParseUserTests.swift in Sources */, + 7050259F2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */, 709B984E2556ECAA00507778 /* ParseGeoPointTests.swift in Sources */, 7044C1BD25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45C2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, @@ -2709,6 +2736,7 @@ 70F03A602780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, 709B98552556ECAA00507778 /* ParseQueryTests.swift in Sources */, 7085DDB526D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */, + 705025A32843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */, 7044C1FB25C5CFAB0011F6E7 /* ParseFileCombineTests.swift in Sources */, 70C5502425B3D8F700B5DBC2 /* ParseAppleTests.swift in Sources */, 917BA4342703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */, @@ -2728,6 +2756,7 @@ 91BB8FD62690D586005A6BA5 /* ParseQueryViewModelTests.swift in Sources */, 709B98542556ECAA00507778 /* ParseInstallationTests.swift in Sources */, 917BA44C2703F10400F8D747 /* ParseAppleAsyncTests.swift in Sources */, + 705025A7284407C4008D6624 /* ParseSchemaTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2796,6 +2825,7 @@ 70F2E2C1254F283000B2EA5C /* AnyCodableTests.swift in Sources */, 7044C22125C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */, 70F2E2B3254F283000B2EA5C /* ParseUserTests.swift in Sources */, + 7050259E2843F0CF008D6624 /* ParseSchemaAsyncTests.swift in Sources */, 70F2E2C0254F283000B2EA5C /* MockURLResponse.swift in Sources */, 7044C1BC25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45B2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, @@ -2804,6 +2834,7 @@ 70F03A5F2780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, 70F2E2BF254F283000B2EA5C /* MockURLProtocol.swift in Sources */, 7085DDB426D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */, + 705025A22843F0E7008D6624 /* ParseSchemaCombineTests.swift in Sources */, 7044C1FA25C5CFAB0011F6E7 /* ParseFileCombineTests.swift in Sources */, 70C5502325B3D8F700B5DBC2 /* ParseAppleTests.swift in Sources */, 917BA4332703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */, @@ -2823,6 +2854,7 @@ 91BB8FD52690D586005A6BA5 /* ParseQueryViewModelTests.swift in Sources */, 70F2E2B9254F283000B2EA5C /* KeychainStoreTests.swift in Sources */, 917BA44B2703F10400F8D747 /* ParseAppleAsyncTests.swift in Sources */, + 705025A6284407C4008D6624 /* ParseSchemaTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2858,6 +2890,7 @@ 91679D67268E596300F71809 /* ParseVersion.swift in Sources */, 91285B1F26990D7F0051B544 /* ParsePolygon.swift in Sources */, 91BB8FCD2690AC99005A6BA5 /* QueryViewModel.swift in Sources */, + 705025AC28441C96008D6624 /* ParseFieldOptions.swift in Sources */, 7085DD9726CBF3A70033B977 /* Documentation.docc in Sources */, F97B465D24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A625A119430052CB31 /* Operations.swift in Sources */, @@ -3005,6 +3038,7 @@ 91679D66268E596300F71809 /* ParseVersion.swift in Sources */, 91285B1E26990D7F0051B544 /* ParsePolygon.swift in Sources */, 91BB8FCC2690AC99005A6BA5 /* QueryViewModel.swift in Sources */, + 705025AB28441C96008D6624 /* ParseFieldOptions.swift in Sources */, 7085DD9626CBF3A70033B977 /* Documentation.docc in Sources */, F97B465C24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A525A119430052CB31 /* Operations.swift in Sources */, diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index 320850695..b0255c80b 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -8,88 +8,64 @@ import Foundation -struct ParseField: Codable { +public struct ParseField: Codable, Equatable { var __op: Operation? // swiftlint:disable:this identifier_name - var type: ParseFieldType? + var type: FieldType? var required: Bool? var defaultValue: AnyCodable? var targetClass: String? - init(operation: Operation) { - __op = operation - } - - init(type: ParseFieldType, options: ParseFieldOptions) where V: Codable { - self.type = type - self.required = options.required - if let defaultValue = options.defaultValue { - self.defaultValue = AnyCodable(defaultValue) - } - } - - init(type: ParseFieldType, options: ParseFieldOptions) throws where V: ParseObject { - self.type = type - self.required = options.required - if let defaultValue = options.defaultValue { - self.defaultValue = AnyCodable(try defaultValue.toPointer()) - } + /// Field types available in `ParseSchema`. + public enum FieldType: String, Codable { + /// A string type. + case string = "String" + /// A number type. + case number = "Number" + /// A boolean type. + case boolean = "Boolean" + /// A date type. + case date = "Date" + /// A file type. + case file = "File" + /// A geoPoint type. + case geoPoint = "GeoPoint" + /// A polygon type. + case polygon = "Polygon" + /// An array type. + case array = "Array" + /// An object type. + case object = "Object" + /// A pointer type. + case pointer = "Pointer" + /// A relation type. + case relation = "Relation" + /// A bytes type. + case bytes = "Bytes" + /// A acl type. + case acl = "ACL" } - init(type: ParseFieldType, - target: T? = nil) where T: ParseObject { - self.type = type - self.targetClass = target?.className - } - - init(type: ParseFieldType, - target: Pointer? = nil) where T: ParseObject { - self.type = type - self.targetClass = target?.className + init(operation: Operation) { + __op = operation } - init(type: ParseFieldType, - target: T? = nil, - options: ParseFieldOptions) where T: ParseObject, V: Codable { + init(type: FieldType, options: ParseFieldOptions) where V: Codable { self.type = type - self.targetClass = target?.className self.required = options.required if let defaultValue = options.defaultValue { self.defaultValue = AnyCodable(defaultValue) } } - init(type: ParseFieldType, - target: Pointer? = nil, - options: ParseFieldOptions) where T: ParseObject, V: Codable { + init(type: FieldType, + options: ParseFieldOptions) where T: ParseObject { self.type = type - self.targetClass = target?.className + self.targetClass = T.className self.required = options.required if let defaultValue = options.defaultValue { self.defaultValue = AnyCodable(defaultValue) } } - - init(type: ParseFieldType, - target: T? = nil, - options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { - self.type = type - self.targetClass = target?.className - self.required = options.required - if let defaultValue = options.defaultValue { - self.defaultValue = AnyCodable(try defaultValue.toPointer()) - } - } - - init(type: ParseFieldType, - target: Pointer? = nil, - options: ParseFieldOptions) throws where T: ParseObject, V: ParseObject { - self.type = type - self.targetClass = target?.className - self.required = options.required - if let defaultValue = options.defaultValue { - self.defaultValue = AnyCodable(try defaultValue.toPointer()) - } - } } // MARK: CustomDebugStringConvertible @@ -110,47 +86,3 @@ extension ParseField { debugDescription } } - -/// The options for a field in `ParseSchema` -public struct ParseFieldOptions: Codable { - /// Specifies if a field is required. - var required: Bool = false - - /// The default value for a field. - var defaultValue: V? - - public init(required: Bool = false, defauleValue: V? = nil) { - self.required = required - self.defaultValue = defauleValue - } -} - -/// Field types available in `ParseSchema`. -public enum ParseFieldType: String, Codable { - /// A string type. - case string = "String" - /// A number type. - case number = "Number" - /// A boolean type. - case boolean = "Boolean" - /// A date type. - case date = "Date" - /// A file type. - case file = "File" - /// A geoPoint type. - case geoPoint = "GeoPoint" - /// A polygon type. - case polygon = "Polygon" - /// An array type. - case array = "Array" - /// An object type. - case object = "Object" - /// A pointer type. - case pointer = "Pointer" - /// A relation type. - case relation = "Relation" - /// A bytes type. - case bytes = "Bytes" - /// A acl type. - case acl = "ACL" -} diff --git a/Sources/ParseSwift/Types/ParseFieldOptions.swift b/Sources/ParseSwift/Types/ParseFieldOptions.swift new file mode 100644 index 000000000..5445c86bb --- /dev/null +++ b/Sources/ParseSwift/Types/ParseFieldOptions.swift @@ -0,0 +1,41 @@ +// +// ParseFieldOptions.swift +// ParseSwift +// +// Created by Corey Baker on 5/29/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation + +/// The options for a field in `ParseSchema` +public struct ParseFieldOptions: Codable { + /// Specifies if a field is required. + public var required: Bool = false + + /// The default value for a field. + public var defaultValue: V? + + /** + Create new options for a `ParseSchema` field. + - parameter required: Specify if the field is required. Defaults to **false**. + - parameter defauleValue: The default value for the field. Defaults to **nil**. + */ + public init(required: Bool = false, defauleValue: V? = nil) { + self.required = required + self.defaultValue = defauleValue + } +} + +extension ParseFieldOptions where V: ParseObject { + /** + Create new options for a `ParseSchema` field. + - parameter required: Specify if the field is required. Defaults to **false**. + - parameter defauleValue: The default value for the field. Defaults to **nil**. + - throws: An error of `ParseError` type. + */ + public init(required: Bool = false, defauleValue: V? = nil) throws { + self.required = required + self.defaultValue = try defauleValue?.toPointer().toObject() + } +} diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 14784a247..5f641ad34 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -73,27 +73,12 @@ public extension ParseSchema { self.classLevelPermissions = classLevelPermissions } - /** - Add an index to create/update a `ParseSchema`. - - - parameter name: Name of the index that will be created/updated in the schema on Parse Server. - - parameter field: The **field** to apply the `ParseIndex` to. - - parameter index: The **index** to create. - - returns: A mutated instance of `ParseSchema` for easy chaining. - */ - func addIndex(_ name: String, - field: String, - index: Encodable) -> Self { - var mutableSchema = self - mutableSchema.pendingIndexes[name] = [field: AnyCodable(index)] - return mutableSchema - } - /** Add a Field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. + - parameter type: The `ParseField.FieldType` of the field that will be created/updated + in the schema on Parse Server. - parameter target: The target `ParseObject` of the field that will be created/updated in the schema on Parse Server. - parameter options: The `ParseFieldOptions` of the field that will be created/updated in @@ -102,17 +87,17 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. */ - func addField(_ name: String, - type: ParseFieldType, - target: T, - options: ParseFieldOptions) throws -> Self where T: ParseObject { + func addField(_ name: String, + type: ParseField.FieldType, + options: ParseFieldOptions) throws -> Self where T: ParseObject { switch type { case .pointer: - return try addPointer(name, target: target, options: options) + return addPointer(name, options: options) case .relation: - return try addRelation(name, target: target) + return addRelation(name, options: options) default: - return addField(name, type: type, options: options) + throw ParseError(code: .unknownError, + message: "The type \"\(type)\" isn't supported by this method") } } @@ -120,14 +105,15 @@ public extension ParseSchema { Add a Field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter type: The `ParseFieldType` of the field that will be created/updated in the schema on Parse Server. + - parameter type: The `ParseField.FieldType` of the field that will be created/updated + in the schema on Parse Server. - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. - warning: The use of `options` requires Parse Server 3.7.0+. */ func addField(_ name: String, - type: ParseFieldType, + type: ParseField.FieldType, options: ParseFieldOptions) -> Self { var mutableSchema = self let field = ParseField(type: type, options: options) @@ -153,14 +139,10 @@ public extension ParseSchema { - throws: An error of type `ParseError`. - warning: The use of `options` requires Parse Server 3.7.0+. */ - func addPointer(_ name: String, - target: T?, - options: ParseFieldOptions) throws -> Self where T: ParseObject { - guard let target = target else { - throw ParseError(code: .unknownError, message: "Target must not be nil") - } + func addPointer(_ name: String, + options: ParseFieldOptions) -> Self where T: ParseObject { - let field = ParseField(type: .pointer, target: target, options: options) + let field = ParseField(type: .pointer, options: options) var mutableSchema = self if mutableSchema.fields != nil { mutableSchema.fields?[name] = field @@ -175,19 +157,16 @@ public extension ParseSchema { Add a Relation field to create/update a `ParseSchema`. - parameter name: Name of the field that will be created/updated in the schema on Parse Server. - - parameter target: The target `ParseObject` of the field that will be created/updated in + - parameter options: The `ParseFieldOptions` of the field that will be created/updated in the schema on Parse Server. Defaults to **nil**. - returns: A mutated instance of `ParseSchema` for easy chaining. - throws: An error of type `ParseError`. */ func addRelation(_ name: String, - target: T?) throws -> Self where T: ParseObject { - guard let target = target else { - throw ParseError(code: .unknownError, message: "Target must not be nil") - } + options: ParseFieldOptions) -> Self where T: ParseObject { - let field = ParseField(type: .relation, target: target) + let field = ParseField(type: .relation, options: options) var mutableSchema = self if mutableSchema.fields != nil { mutableSchema.fields?[name] = field @@ -216,6 +195,22 @@ public extension ParseSchema { return mutableSchema } + /** + Add an index to create/update a `ParseSchema`. + + - parameter name: Name of the index that will be created/updated in the schema on Parse Server. + - parameter field: The **field** to apply the `ParseIndex` to. + - parameter index: The **index** to create. + - returns: A mutated instance of `ParseSchema` for easy chaining. + */ + func addIndex(_ name: String, + field: String, + index: Encodable) -> Self { + var mutableSchema = self + mutableSchema.pendingIndexes[name] = [field: AnyCodable(index)] + return mutableSchema + } + /** Delete an index in the `ParseSchema`. @@ -232,14 +227,6 @@ public extension ParseSchema { // MARK: Convenience extension ParseSchema { - static var endpoint: API.Endpoint { - .schema(className: className) - } - - static var endpointPurge: API.Endpoint { - .purge(className: className) - } - var endpoint: API.Endpoint { .schema(className: className) } diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 187008251..4ab37760e 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -152,6 +152,11 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp4 = ParseCLP().addPointerFields(newField, on: .create) XCTAssertEqual(clp4.getPointerFields(.create), newField) + + let clp5 = ParseCLP().setAccess(true, on: .create, for: "yo") + .setPointerFields(fields, on: .create) + XCTAssertEqual(clp5.getPointerFields(.create), fields) + XCTAssertEqual(clp5.hasAccess(.create, for: "yo"), true) } func testPointerFieldsEncode() throws { @@ -183,6 +188,10 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length let clp4 = ParseCLP().addProtectedFieldsPublic(newField) XCTAssertEqual(clp4.getProtectedFieldsPublic(), newField) + + let clp5 = clp.setProtectedFieldsRequiresAuthentication(newField) + XCTAssertEqual(clp5.getProtectedFieldsPublic(), fields) + XCTAssertEqual(clp5.getProtectedFieldsRequiresAuthentication(), newField) } func testProtectedFieldsRequiresAuthentication() throws { diff --git a/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift new file mode 100644 index 000000000..f4b63e66f --- /dev/null +++ b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift @@ -0,0 +1,135 @@ +// +// ParseSchemaAsyncTests.swift +// ParseSwift +// +// Created by Corey Baker on 5/29/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +#if compiler(>=5.5.2) && canImport(_Concurrency) +import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif +import XCTest +@testable import ParseSwift + +class ParseSchemaAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length + struct GameScore: ParseObject, ParseQueryScorable { + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var score: Double? + var originalData: Data? + + //: Your own properties + var points: Int + var isCounts: Bool? + + //: a custom initializer + init() { + self.points = 5 + } + init(points: Int) { + self.points = points + } + } + + struct User: ParseUser { + + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // These are required by ParseUser + var username: String? + var email: String? + var emailVerified: Bool? + var password: String? + var authData: [String: [String: String]?]? + + // Your custom keys + var customKey: String? + + init() { } + init(objectId: String) { + self.objectId = objectId + } + } + + struct Role: ParseRole { + + // required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // provided by Role + var name: String? + } + + override func setUpWithError() throws { + try super.setUpWithError() + guard let url = URL(string: "http://localhost:1337/1") else { + XCTFail("Should create valid URL") + return + } + ParseSwift.initialize(applicationId: "applicationId", + clientKey: "clientKey", + masterKey: "masterKey", + serverURL: url, + testing: true) + } + + let objectId = "1234" + let user = User(objectId: "1234") + + override func tearDownWithError() throws { + try super.tearDownWithError() + MockURLProtocol.removeAll() + #if !os(Linux) && !os(Android) && !os(Windows) + try KeychainStore.shared.deleteAll() + #endif + try ParseStorage.shared.deleteAll() + } + +/* + @MainActor + func testCreate() async throws { + MockURLProtocol.removeAll() + + var schema = ParseSchema() + schema.fields + installation.installationId = "123" + + var serverResponse = installation + serverResponse.objectId = "yolo" + serverResponse.createdAt = Date() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try serverResponse.getEncoder().encode(serverResponse, skipKeys: .none) + //Get dates in correct format from ParseDecoding strategy + serverResponse = try serverResponse.getDecoder().decode(Installation.self, from: encoded) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let saved = try await installation.create() + XCTAssert(saved.hasSameObjectId(as: serverResponse)) + XCTAssert(saved.hasSameInstallationId(as: serverResponse)) + XCTAssertEqual(saved.createdAt, serverResponse.createdAt) + XCTAssertEqual(saved.updatedAt, serverResponse.createdAt) + } + */ +} +#endif diff --git a/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift new file mode 100644 index 000000000..fd38369b9 --- /dev/null +++ b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift @@ -0,0 +1,19 @@ +// +// ParseSchemaCombineTests.swift +// ParseSwift +// +// Created by Corey Baker on 5/29/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +#if canImport(Combine) + +import Foundation +import XCTest +import Combine +@testable import ParseSwift + +class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_length + +} +#endif diff --git a/Tests/ParseSwiftTests/ParseSchemaTests.swift b/Tests/ParseSwiftTests/ParseSchemaTests.swift new file mode 100644 index 000000000..caaf7bf86 --- /dev/null +++ b/Tests/ParseSwiftTests/ParseSchemaTests.swift @@ -0,0 +1,218 @@ +// +// ParseSchemaTests.swift +// ParseSwift +// +// Created by Corey Baker on 5/29/22. +// Copyright © 2022 Parse Community. All rights reserved. +// + +import Foundation +import XCTest +@testable import ParseSwift + +class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length + struct GameScore: ParseObject { + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var score: Double? + var originalData: Data? + + //: Your own properties + var points: Int + + //: a custom initializer + init() { + self.points = 5 + } + init(points: Int) { + self.points = points + } + } + + struct GameScore2: ParseObject { + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var score: Double? + var originalData: Data? + + //: Your own properties + var points: Int + + //: a custom initializer + init() { + self.points = 10 + } + init(points: Int) { + self.points = points + } + init(objectId: String, points: Int) { + self.objectId = objectId + self.points = points + } + } + + struct User: ParseUser { + + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // These are required by ParseUser + var username: String? + var email: String? + var emailVerified: Bool? + var password: String? + var authData: [String: [String: String]?]? + + // Your custom keys + var customKey: String? + + init() { } + init(objectId: String) { + self.objectId = objectId + } + } + + struct Role: ParseRole { + + // required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + + // provided by Role + var name: String? + } + + override func setUpWithError() throws { + try super.setUpWithError() + guard let url = URL(string: "http://localhost:1337/1") else { + XCTFail("Should create valid URL") + return + } + ParseSwift.initialize(applicationId: "applicationId", + clientKey: "clientKey", + masterKey: "masterKey", + serverURL: url, + testing: true) + } + + let objectId = "1234" + let user = User(objectId: "1234") + + override func tearDownWithError() throws { + try super.tearDownWithError() + MockURLProtocol.removeAll() + #if !os(Linux) && !os(Android) && !os(Windows) + try KeychainStore.shared.deleteAll() + #endif + try ParseStorage.shared.deleteAll() + } + + func testInitializer() throws { + let clp = ParseCLP(requiresAuthentication: true, publicAccess: true) + let schema = ParseSchema(classLevelPermissions: clp) + XCTAssertEqual(schema.className, GameScore.className) + XCTAssertEqual(schema.classLevelPermissions, clp) + } + + func testAddField() throws { + let schema = ParseSchema() + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("b", + type: .pointer, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("c", + type: .date, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("d", + type: .acl, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("e", + type: .array, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("f", + type: .bytes, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("g", + type: .object, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("h", + type: .file, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("i", + type: .geoPoint, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("j", + type: .relation, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("k", + type: .polygon, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("l", + type: .boolean, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("m", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: nil)) + XCTAssertEqual(schema.fields?["a"]?.type, .string) + XCTAssertEqual(schema.fields?["b"]?.type, .pointer) + XCTAssertEqual(schema.fields?["c"]?.type, .date) + XCTAssertEqual(schema.fields?["d"]?.type, .acl) + XCTAssertEqual(schema.fields?["e"]?.type, .array) + XCTAssertEqual(schema.fields?["f"]?.type, .bytes) + XCTAssertEqual(schema.fields?["g"]?.type, .object) + XCTAssertEqual(schema.fields?["h"]?.type, .file) + XCTAssertEqual(schema.fields?["i"]?.type, .geoPoint) + XCTAssertEqual(schema.fields?["j"]?.type, .relation) + XCTAssertEqual(schema.fields?["k"]?.type, .polygon) + XCTAssertEqual(schema.fields?["l"]?.type, .boolean) + XCTAssertEqual(schema.fields?["m"]?.type, .number) + } + + func testAddPointer() throws { + let gameScore2 = GameScore2(objectId: "yolo", points: 12) + let options = try ParseFieldOptions(required: false, defauleValue: gameScore2) + let schema = ParseSchema() + .addPointer("a", + options: options) + XCTAssertEqual(schema.fields?["a"]?.type, .pointer) + XCTAssertEqual(schema.fields?["a"]?.targetClass, gameScore2.className) + } + + func testAddRelation() throws { + let options = try ParseFieldOptions(required: false, defauleValue: nil) + let schema = ParseSchema() + .addRelation("a", + options: options) + XCTAssertEqual(schema.fields?["a"]?.type, .relation) + XCTAssertEqual(schema.fields?["a"]?.targetClass, GameScore2().className) + } + + func testDeleteField() throws { + var schema = ParseSchema() + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .deleteField("a") + let delete = ParseField(operation: .delete) + XCTAssertEqual(schema.fields?["a"], delete) + + schema = schema.deleteField("b") + XCTAssertEqual(schema.fields?["a"], delete) + XCTAssertEqual(schema.fields?["b"], delete) + } +} From ff3f98331e502f435a897f2abd4f12cdbbf92fc6 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 00:44:02 -0400 Subject: [PATCH 50/55] finish ParseSchema tests --- .../Contents.swift | 17 + ParseSwift.xcodeproj/project.pbxproj | 2 +- Sources/ParseSwift/Types/ParseError.swift | 2 +- Sources/ParseSwift/Types/ParseField.swift | 4 +- .../ParseSwift/Types/ParseFieldOptions.swift | 19 + Sources/ParseSwift/Types/ParseSchema.swift | 29 +- .../ParseSchemaAsyncTests.swift | 296 ++++++++++--- .../ParseSchemaCombineTests.swift | 399 ++++++++++++++++++ Tests/ParseSwiftTests/ParseSchemaTests.swift | 191 ++++++--- 9 files changed, 834 insertions(+), 125 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index 599cb15cd..ecbde8a3a 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -136,6 +136,23 @@ gameScoreSchema.update { result in } } +//: Indexes can also be deleted. +gameScoreSchema = gameScoreSchema.deleteIndex("myIndex") + +//: Next, we need to update the schema on the server with the changes. +gameScoreSchema.update { result in + switch result { + case .success(let updatedSchema): + print("Check GameScore2 in Dashboard. \nThe updated schema: \(updatedSchema)") + /*: + Updated the current gameScoreSchema with the newest. + */ + gameScoreSchema = updatedSchema + case .failure(let error): + print("Couldn't update schema: \(error)") + } +} + /*: Fields can also be deleted on a schema. Lets remove the **data** field since it's not going being used. diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 9dd6a6bfd..e1999a1cc 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -1355,9 +1355,9 @@ 91BB8FD32690D586005A6BA5 /* ParseQueryViewModelTests.swift */, 70D1BD8625B8C37200A42E7C /* ParseRelationTests.swift */, 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */, - 705025A4284407C4008D6624 /* ParseSchemaTests.swift */, 7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */, 705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */, + 705025A4284407C4008D6624 /* ParseSchemaTests.swift */, 70C5504525B40D5200B5DBC2 /* ParseSessionTests.swift */, 917BA4512703F55700F8D747 /* ParseTwitterAsyncTests.swift */, 89899D9E26045998002E2043 /* ParseTwitterCombineTests.swift */, diff --git a/Sources/ParseSwift/Types/ParseError.swift b/Sources/ParseSwift/Types/ParseError.swift index 9895c934f..7b6fc8fbe 100644 --- a/Sources/ParseSwift/Types/ParseError.swift +++ b/Sources/ParseSwift/Types/ParseError.swift @@ -11,7 +11,7 @@ import Foundation /** An object with a Parse code and message. */ -public struct ParseError: ParseType, Decodable, Swift.Error { +public struct ParseError: ParseType, Equatable, Decodable, Swift.Error { /// The value representing the error from the Parse Server. public let code: Code /// The text representing the error from the Parse Server. diff --git a/Sources/ParseSwift/Types/ParseField.swift b/Sources/ParseSwift/Types/ParseField.swift index b0255c80b..a0b2c6297 100644 --- a/Sources/ParseSwift/Types/ParseField.swift +++ b/Sources/ParseSwift/Types/ParseField.swift @@ -73,10 +73,10 @@ extension ParseField { public var debugDescription: String { guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), let descriptionString = String(data: descriptionData, encoding: .utf8) else { - return "()" + return "ParseField ()" } - return "(\(descriptionString))" + return "ParseField (\(descriptionString))" } } diff --git a/Sources/ParseSwift/Types/ParseFieldOptions.swift b/Sources/ParseSwift/Types/ParseFieldOptions.swift index 5445c86bb..ce736d875 100644 --- a/Sources/ParseSwift/Types/ParseFieldOptions.swift +++ b/Sources/ParseSwift/Types/ParseFieldOptions.swift @@ -39,3 +39,22 @@ extension ParseFieldOptions where V: ParseObject { self.defaultValue = try defauleValue?.toPointer().toObject() } } + +// MARK: CustomDebugStringConvertible +extension ParseFieldOptions { + public var debugDescription: String { + guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), + let descriptionString = String(data: descriptionData, encoding: .utf8) else { + return "ParseFieldOptions ()" + } + + return "ParseFieldOptions (\(descriptionString))" + } +} + +// MARK: CustomStringConvertible +extension ParseFieldOptions { + public var description: String { + debugDescription + } +} diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 5f641ad34..b63db6902 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -44,10 +44,25 @@ public struct ParseSchema: ParseType, Decodable { Get the current indexes for this `ParseSchema`. - returns: The current indexes. */ - public func getIndexes() -> [String: String] { - var currentIndexes = [String: String]() - indexes?.forEach { (key, value) in - currentIndexes[key] = value.description + public func getIndexes() -> [String: [String: String]] { + var currentIndexes = [String: [String: String]]() + indexes?.forEach { (name, value) in + value.forEach { (field, index) in + if currentIndexes[name] != nil { + currentIndexes[name]?[field] = index.description + } else { + currentIndexes[name] = [field: index.description] + } + } + } + pendingIndexes.forEach { (name, value) in + value.forEach { (field, index) in + if currentIndexes[name] != nil { + currentIndexes[name]?[field] = index.description + } else { + currentIndexes[name] = [field: index.description] + } + } } return currentIndexes } @@ -217,10 +232,10 @@ public extension ParseSchema { - parameter name: Name of the index that will be deleted in the schema on Parse Server. - returns: A mutated instance of `ParseSchema` for easy chaining. */ - func deleteIndex(_ name: String, field: String) -> Self { - let index = AnyCodable(Delete()) + func deleteIndex(_ name: String) -> Self { + let index = ["__op": AnyCodable(Operation.delete.rawValue)] var mutableSchema = self - mutableSchema.pendingIndexes[name] = [field: index] + mutableSchema.pendingIndexes[name] = index return mutableSchema } } diff --git a/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift index f4b63e66f..43ce15682 100644 --- a/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift @@ -37,44 +37,6 @@ class ParseSchemaAsyncTests: XCTestCase { // swiftlint:disable:this type_body_le } } - struct User: ParseUser { - - //: These are required by ParseObject - var objectId: String? - var createdAt: Date? - var updatedAt: Date? - var ACL: ParseACL? - var originalData: Data? - - // These are required by ParseUser - var username: String? - var email: String? - var emailVerified: Bool? - var password: String? - var authData: [String: [String: String]?]? - - // Your custom keys - var customKey: String? - - init() { } - init(objectId: String) { - self.objectId = objectId - } - } - - struct Role: ParseRole { - - // required by ParseObject - var objectId: String? - var createdAt: Date? - var updatedAt: Date? - var ACL: ParseACL? - var originalData: Data? - - // provided by Role - var name: String? - } - override func setUpWithError() throws { try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { @@ -88,9 +50,6 @@ class ParseSchemaAsyncTests: XCTestCase { // swiftlint:disable:this type_body_le testing: true) } - let objectId = "1234" - let user = User(objectId: "1234") - override func tearDownWithError() throws { try super.tearDownWithError() MockURLProtocol.removeAll() @@ -100,36 +59,257 @@ class ParseSchemaAsyncTests: XCTestCase { // swiftlint:disable:this type_body_le try ParseStorage.shared.deleteAll() } -/* + func createDummySchema() -> ParseSchema { + ParseSchema() + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("b", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: 2)) + .deleteField("c") + .addIndex("hello", field: "world", index: "yolo") + } + @MainActor func testCreate() async throws { - MockURLProtocol.removeAll() - var schema = ParseSchema() - schema.fields - installation.installationId = "123" + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let saved = try await schema.create() + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + } + + @MainActor + func testCreateError() async throws { + + let schema = createDummySchema() - var serverResponse = installation - serverResponse.objectId = "yolo" - serverResponse.createdAt = Date() + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") MockURLProtocol.mockRequests { _ in do { - let encoded = try serverResponse.getEncoder().encode(serverResponse, skipKeys: .none) - //Get dates in correct format from ParseDecoding strategy - serverResponse = try serverResponse.getDecoder().decode(Installation.self, from: encoded) + let encoded = try ParseCoding.jsonEncoder().encode(parseError) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil } } - let saved = try await installation.create() - XCTAssert(saved.hasSameObjectId(as: serverResponse)) - XCTAssert(saved.hasSameInstallationId(as: serverResponse)) - XCTAssertEqual(saved.createdAt, serverResponse.createdAt) - XCTAssertEqual(saved.updatedAt, serverResponse.createdAt) + do { + _ = try await schema.create() + XCTFail("Should have thrown error") + } catch { + XCTAssertTrue(error.equalsTo(.invalidSchemaOperation)) + } + } + + @MainActor + func testUpdate() async throws { + + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let saved = try await schema.update() + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + } + + @MainActor + func testUpdateError() async throws { + + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + do { + _ = try await schema.update() + XCTFail("Should have thrown error") + } catch { + XCTAssertTrue(error.equalsTo(.invalidSchemaOperation)) + } + } + + @MainActor + func testFetch() async throws { + + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let saved = try await schema.fetch() + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + } + + @MainActor + func testFetchError() async throws { + + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + do { + _ = try await schema.fetch() + XCTFail("Should have thrown error") + } catch { + XCTAssertTrue(error.equalsTo(.invalidSchemaOperation)) + } + } + + @MainActor + func testPurge() async throws { + + let schema = createDummySchema() + + let serverResponse = NoBody() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + try await schema.purge() + } + + @MainActor + func testPurgeError() async throws { + + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + do { + _ = try await schema.purge() + XCTFail("Should have thrown error") + } catch { + XCTAssertTrue(error.equalsTo(.invalidSchemaOperation)) + } + } + + @MainActor + func testDelete() async throws { + + let schema = createDummySchema() + + let serverResponse = NoBody() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + try await schema.delete() + } + + @MainActor + func testDeleteError() async throws { + + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + do { + _ = try await schema.delete() + XCTFail("Should have thrown error") + } catch { + XCTAssertTrue(error.equalsTo(.invalidSchemaOperation)) + } } - */ } #endif diff --git a/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift index fd38369b9..86d426ae5 100644 --- a/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift @@ -14,6 +14,405 @@ import Combine @testable import ParseSwift class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_length + struct GameScore: ParseObject, ParseQueryScorable { + //: These are required by ParseObject + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var score: Double? + var originalData: Data? + //: Your own properties + var points: Int + var isCounts: Bool? + + //: a custom initializer + init() { + self.points = 5 + } + init(points: Int) { + self.points = points + } + } + + override func setUpWithError() throws { + try super.setUpWithError() + guard let url = URL(string: "http://localhost:1337/1") else { + XCTFail("Should create valid URL") + return + } + ParseSwift.initialize(applicationId: "applicationId", + clientKey: "clientKey", + masterKey: "masterKey", + serverURL: url, + testing: true) + } + + override func tearDownWithError() throws { + try super.tearDownWithError() + MockURLProtocol.removeAll() + #if !os(Linux) && !os(Android) && !os(Windows) + try KeychainStore.shared.deleteAll() + #endif + try ParseStorage.shared.deleteAll() + } + + func createDummySchema() -> ParseSchema { + ParseSchema() + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("b", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: 2)) + .deleteField("c") + .addIndex("hello", field: "world", index: "yolo") + } + + func testCreate() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Save schema") + let publisher = schema.createPublisher() + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + + }, receiveValue: { saved in + + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testCreateError() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Create schema") + let publisher = schema.createPublisher() + .sink(receiveCompletion: { result in + + if case .finished = result { + XCTFail("Should have thrown ParseError") + } + expectation1.fulfill() + + }, receiveValue: { _ in + XCTFail("Should have thrown ParseError") + expectation1.fulfill() + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testUpdate() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Update schema") + let publisher = schema.updatePublisher() + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + + }, receiveValue: { saved in + + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testUpdateError() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Update schema") + let publisher = schema.updatePublisher() + .sink(receiveCompletion: { result in + + if case .finished = result { + XCTFail("Should have thrown ParseError") + } + expectation1.fulfill() + + }, receiveValue: { _ in + XCTFail("Should have thrown ParseError") + expectation1.fulfill() + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testFetch() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Fetch schema") + let publisher = schema.fetchPublisher() + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + + }, receiveValue: { saved in + + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testFetchError() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Fetch schema") + let publisher = schema.fetchPublisher() + .sink(receiveCompletion: { result in + + if case .finished = result { + XCTFail("Should have thrown ParseError") + } + expectation1.fulfill() + + }, receiveValue: { _ in + XCTFail("Should have thrown ParseError") + expectation1.fulfill() + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testPurge() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Purge schema") + let publisher = schema.purgePublisher() + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + + }, receiveValue: { _ in + + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testPurgeError() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Purge schema") + let publisher = schema.purgePublisher() + .sink(receiveCompletion: { result in + + if case .finished = result { + XCTFail("Should have thrown ParseError") + } + expectation1.fulfill() + + }, receiveValue: { _ in + XCTFail("Should have thrown ParseError") + expectation1.fulfill() + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testDelete() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + var serverResponse = schema + serverResponse.indexes = schema.pendingIndexes + serverResponse.pendingIndexes.removeAll() + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Delete schema") + let publisher = schema.deletePublisher() + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + } + expectation1.fulfill() + + }, receiveValue: { _ in + + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + + func testDeleteError() async throws { + var subscriptions = Set() + let schema = createDummySchema() + + let parseError = ParseError(code: .invalidSchemaOperation, + message: "Problem with schema") + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(parseError) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Delete schema") + let publisher = schema.deletePublisher() + .sink(receiveCompletion: { result in + + if case .finished = result { + XCTFail("Should have thrown ParseError") + } + expectation1.fulfill() + + }, receiveValue: { _ in + XCTFail("Should have thrown ParseError") + expectation1.fulfill() + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } } #endif diff --git a/Tests/ParseSwiftTests/ParseSchemaTests.swift b/Tests/ParseSwiftTests/ParseSchemaTests.swift index caaf7bf86..678c7016d 100644 --- a/Tests/ParseSwiftTests/ParseSchemaTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaTests.swift @@ -21,12 +21,10 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length var originalData: Data? //: Your own properties - var points: Int + var points: Int? //: a custom initializer - init() { - self.points = 5 - } + init() { } init(points: Int) { self.points = points } @@ -42,12 +40,10 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length var originalData: Data? //: Your own properties - var points: Int + var points: Int? //: a custom initializer - init() { - self.points = 10 - } + init() { } init(points: Int) { self.points = points } @@ -57,44 +53,6 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length } } - struct User: ParseUser { - - //: These are required by ParseObject - var objectId: String? - var createdAt: Date? - var updatedAt: Date? - var ACL: ParseACL? - var originalData: Data? - - // These are required by ParseUser - var username: String? - var email: String? - var emailVerified: Bool? - var password: String? - var authData: [String: [String: String]?]? - - // Your custom keys - var customKey: String? - - init() { } - init(objectId: String) { - self.objectId = objectId - } - } - - struct Role: ParseRole { - - // required by ParseObject - var objectId: String? - var createdAt: Date? - var updatedAt: Date? - var ACL: ParseACL? - var originalData: Data? - - // provided by Role - var name: String? - } - override func setUpWithError() throws { try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { @@ -108,9 +66,6 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length testing: true) } - let objectId = "1234" - let user = User(objectId: "1234") - override func tearDownWithError() throws { try super.tearDownWithError() MockURLProtocol.removeAll() @@ -120,21 +75,48 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length try ParseStorage.shared.deleteAll() } + func createDummySchema() -> ParseSchema { + let fields = Set(["world"]) + let clp = ParseCLP() + .setPointerFields(fields, on: .create) + .setWriteAccessPublic(true, canAddField: true) + + let schema = ParseSchema(classLevelPermissions: clp) + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("b", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: 2)) + .deleteField("c") + .addIndex("hello", field: "world", index: "yolo") + return schema + } + func testInitializer() throws { let clp = ParseCLP(requiresAuthentication: true, publicAccess: true) let schema = ParseSchema(classLevelPermissions: clp) XCTAssertEqual(schema.className, GameScore.className) + XCTAssertEqual(ParseSchema.className, GameScore.className) XCTAssertEqual(schema.classLevelPermissions, clp) } + func testSchemaEncode() throws { + let schema = createDummySchema() + // swiftlint:disable:next line_length + let expected = "ParseSchema ({\"classLevelPermissions\":{\"addField\":{\"*\":true},\"create\":{\"*\":true,\"pointerFields\":[\"world\"]},\"delete\":{\"*\":true},\"update\":{\"*\":true}},\"className\":\"GameScore\",\"fields\":{\"a\":{\"required\":false,\"type\":\"String\"},\"b\":{\"defaultValue\":2,\"required\":false,\"type\":\"Number\"},\"c\":{\"__op\":\"Delete\"}}})" + XCTAssertEqual(schema.description, expected) + } + func testAddField() throws { - let schema = ParseSchema() + let options = try ParseFieldOptions(required: false, defauleValue: nil) + let schema = try ParseSchema() .addField("a", type: .string, options: ParseFieldOptions(required: false, defauleValue: nil)) .addField("b", type: .pointer, - options: ParseFieldOptions(required: false, defauleValue: nil)) + options: options) .addField("c", type: .date, options: ParseFieldOptions(required: false, defauleValue: nil)) @@ -158,7 +140,7 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length options: ParseFieldOptions(required: false, defauleValue: nil)) .addField("j", type: .relation, - options: ParseFieldOptions(required: false, defauleValue: nil)) + options: options) .addField("k", type: .polygon, options: ParseFieldOptions(required: false, defauleValue: nil)) @@ -183,6 +165,27 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(schema.fields?["m"]?.type, .number) } + func testAddFieldWrongOptionsError() throws { + let options = try ParseFieldOptions(required: false, defauleValue: nil) + XCTAssertThrowsError(try ParseSchema() + .addField("b", + type: .string, + options: options)) + } + + func testGetFields() throws { + let schema = ParseSchema() + .addField("a", + type: .string, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("b", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: 2)) + let fields = schema.getFields() + XCTAssertEqual(fields["a"], "ParseField ({\"required\":false,\"type\":\"String\"})") + XCTAssertEqual(fields["b"], "ParseField ({\"defaultValue\":2,\"required\":false,\"type\":\"Number\"})") + } + func testAddPointer() throws { let gameScore2 = GameScore2(objectId: "yolo", points: 12) let options = try ParseFieldOptions(required: false, defauleValue: gameScore2) @@ -191,6 +194,21 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length options: options) XCTAssertEqual(schema.fields?["a"]?.type, .pointer) XCTAssertEqual(schema.fields?["a"]?.targetClass, gameScore2.className) + guard let value = schema.fields?["a"]?.defaultValue?.value as? GameScore2 else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(try value.toPointer(), try gameScore2.toPointer()) + + let schema2 = schema.addPointer("b", + options: options) + XCTAssertEqual(schema2.fields?["b"]?.type, .pointer) + XCTAssertEqual(schema2.fields?["b"]?.targetClass, gameScore2.className) + guard let value2 = schema2.fields?["b"]?.defaultValue?.value as? GameScore2 else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(try value2.toPointer(), try gameScore2.toPointer()) } func testAddRelation() throws { @@ -199,14 +217,16 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length .addRelation("a", options: options) XCTAssertEqual(schema.fields?["a"]?.type, .relation) - XCTAssertEqual(schema.fields?["a"]?.targetClass, GameScore2().className) + XCTAssertEqual(schema.fields?["a"]?.targetClass, GameScore2.className) + + let schema2 = schema.addRelation("b", + options: options) + XCTAssertEqual(schema2.fields?["b"]?.type, .relation) + XCTAssertEqual(schema2.fields?["b"]?.targetClass, GameScore2.className) } func testDeleteField() throws { var schema = ParseSchema() - .addField("a", - type: .string, - options: ParseFieldOptions(required: false, defauleValue: nil)) .deleteField("a") let delete = ParseField(operation: .delete) XCTAssertEqual(schema.fields?["a"], delete) @@ -215,4 +235,63 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(schema.fields?["a"], delete) XCTAssertEqual(schema.fields?["b"], delete) } + + func testAddIndexes() throws { + let schema = ParseSchema() + .addIndex("hello", field: "world", index: "yolo") + .addIndex("next", field: "place", index: "home") + let indexes = schema.getIndexes() + guard let firstIndex = indexes["hello"]?["world"], + let secondIndex = indexes["next"]?["place"] else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(firstIndex, "yolo") + XCTAssertEqual(secondIndex, "home") + + let alreadyStoredIndexes: [String: [String: AnyCodable]] = [ + "meta": ["world": "peace"], + "stop": ["being": "greedy"] + ] + var schema2 = ParseSchema() + schema2.indexes = alreadyStoredIndexes + let indexes2 = schema2.getIndexes() + guard let firstIndex2 = indexes2["meta"]?["world"], + let secondIndex2 = indexes2["stop"]?["being"] else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(firstIndex2, "peace") + XCTAssertEqual(secondIndex2, "greedy") + + schema2 = schema2 + .addIndex("hello", field: "world", index: "yolo") + .addIndex("next", field: "place", index: "home") + let indexes3 = schema2.getIndexes() + guard let firstIndex3 = indexes3["meta"]?["world"], + let secondIndex3 = indexes3["stop"]?["being"], + let thirdIndex3 = indexes["hello"]?["world"], + let fourthIndex3 = indexes["next"]?["place"] else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(firstIndex3, "peace") + XCTAssertEqual(secondIndex3, "greedy") + XCTAssertEqual(thirdIndex3, "yolo") + XCTAssertEqual(fourthIndex3, "home") + } + + func testDeleteIndexes() throws { + let schema = ParseSchema() + .deleteIndex("hello") + .addIndex("next", field: "place", index: "home") + let indexes = schema.getIndexes() + guard let firstIndex = indexes["hello"]?["__op"], + let secondIndex = indexes["next"]?["place"] else { + XCTFail("Should have unwrapped") + return + } + XCTAssertEqual(firstIndex, "Delete") + XCTAssertEqual(secondIndex, "home") + } } From cf2965269cc81770e839b616d7acc0d82156ba5f Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 01:08:00 -0400 Subject: [PATCH 51/55] increase coverage --- ParseSwift.xcodeproj/project.pbxproj | 10 ------- Sources/ParseSwift/Protocols/ParseIndex.swift | 16 ---------- Sources/ParseSwift/Types/ParseSchema.swift | 14 ++------- .../ParseSchemaAsyncTests.swift | 29 +++++++++++++++++++ Tests/ParseSwiftTests/ParseSchemaTests.swift | 6 ++++ 5 files changed, 38 insertions(+), 37 deletions(-) delete mode 100644 Sources/ParseSwift/Protocols/ParseIndex.swift diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index e1999a1cc..ef10866a0 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -374,10 +374,6 @@ 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A148B2839A1DB00BF85E5 /* Operation.swift */; }; - 709A14912839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; - 709A14922839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; - 709A14932839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; - 709A14942839A60600BF85E5 /* ParseIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A14902839A60600BF85E5 /* ParseIndex.swift */; }; 709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A12839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; 709A14A22839CABD00BF85E5 /* ParseCLP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709A149F2839CABD00BF85E5 /* ParseCLP.swift */; }; @@ -1021,7 +1017,6 @@ 709A148128395ED100BF85E5 /* ParseSchema+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+async.swift"; sourceTree = ""; }; 709A148628396B1C00BF85E5 /* ParseField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseField.swift; sourceTree = ""; }; 709A148B2839A1DB00BF85E5 /* Operation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; - 709A14902839A60600BF85E5 /* ParseIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseIndex.swift; sourceTree = ""; }; 709A149F2839CABD00BF85E5 /* ParseCLP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLP.swift; sourceTree = ""; }; 709A14A4283AAF4C00BF85E5 /* ParseSchema+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseSchema+combine.swift"; sourceTree = ""; }; 709B40C0268F999000ED2EAC /* IOS13Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS13Tests.swift; sourceTree = ""; }; @@ -1471,7 +1466,6 @@ F97B45C524D9C6F200F4A88B /* Fetchable.swift */, 705A9A2E25991C1400B3547F /* Fileable.swift */, 70BC988F252A5B5C00FF3074 /* Objectable.swift */, - 709A14902839A60600BF85E5 /* ParseIndex.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 70647E9B259E3A9A004C1004 /* ParseType.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, @@ -2295,7 +2289,6 @@ F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A325A119430052CB31 /* Operations.swift in Sources */, 91BB8FCF2690BA70005A6BA5 /* QueryObservable.swift in Sources */, - 709A14912839A60600BF85E5 /* ParseIndex.swift in Sources */, 70F03A232780BDE200E5AFB4 /* ParseGoogle.swift in Sources */, 709A148228395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769826BD917500F86F71 /* Query+async.swift in Sources */, @@ -2541,7 +2534,6 @@ F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 700395A425A119430052CB31 /* Operations.swift in Sources */, 91BB8FD02690BA70005A6BA5 /* QueryObservable.swift in Sources */, - 709A14922839A60600BF85E5 /* ParseIndex.swift in Sources */, 7045769926BD917500F86F71 /* Query+async.swift in Sources */, 709A148328395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 703B094F26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, @@ -2894,7 +2886,6 @@ 7085DD9726CBF3A70033B977 /* Documentation.docc in Sources */, F97B465D24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A625A119430052CB31 /* Operations.swift in Sources */, - 709A14942839A60600BF85E5 /* ParseIndex.swift in Sources */, 91BB8FD22690BA70005A6BA5 /* QueryObservable.swift in Sources */, 709A148528395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769B26BD917500F86F71 /* Query+async.swift in Sources */, @@ -3042,7 +3033,6 @@ 7085DD9626CBF3A70033B977 /* Documentation.docc in Sources */, F97B465C24D9C78C00F4A88B /* Increment.swift in Sources */, 700395A525A119430052CB31 /* Operations.swift in Sources */, - 709A14932839A60600BF85E5 /* ParseIndex.swift in Sources */, 91BB8FD12690BA70005A6BA5 /* QueryObservable.swift in Sources */, 709A148428395ED100BF85E5 /* ParseSchema+async.swift in Sources */, 7045769A26BD917500F86F71 /* Query+async.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParseIndex.swift b/Sources/ParseSwift/Protocols/ParseIndex.swift deleted file mode 100644 index 36381980b..000000000 --- a/Sources/ParseSwift/Protocols/ParseIndex.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// ParseIndex.swift -// ParseSwift -// -// Created by Corey Baker on 5/21/22. -// Copyright © 2022 Parse Community. All rights reserved. -// - -import Foundation - -/** - `ParseIndex` is used to create/update an index on a field of `ParseSchema`. - The "property name" should match the "field name" the index should be added to. - The "property value" should be the "type" of index to add which is usually a **String** or **Int*. -*/ -public protocol ParseIndex: Codable { } diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index b63db6902..6a86d73b1 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -48,20 +48,12 @@ public struct ParseSchema: ParseType, Decodable { var currentIndexes = [String: [String: String]]() indexes?.forEach { (name, value) in value.forEach { (field, index) in - if currentIndexes[name] != nil { - currentIndexes[name]?[field] = index.description - } else { - currentIndexes[name] = [field: index.description] - } + currentIndexes[name] = [field: index.description] } } pendingIndexes.forEach { (name, value) in value.forEach { (field, index) in - if currentIndexes[name] != nil { - currentIndexes[name]?[field] = index.description - } else { - currentIndexes[name] = [field: index.description] - } + currentIndexes[name] = [field: index.description] } } return currentIndexes @@ -214,7 +206,7 @@ public extension ParseSchema { Add an index to create/update a `ParseSchema`. - parameter name: Name of the index that will be created/updated in the schema on Parse Server. - - parameter field: The **field** to apply the `ParseIndex` to. + - parameter field: The **field** the index should be added to. - parameter index: The **index** to create. - returns: A mutated instance of `ParseSchema` for easy chaining. */ diff --git a/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift index 43ce15682..23e482a83 100644 --- a/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaAsyncTests.swift @@ -148,6 +148,35 @@ class ParseSchemaAsyncTests: XCTestCase { // swiftlint:disable:this type_body_le XCTAssertTrue(saved.pendingIndexes.isEmpty) } + @MainActor + func testUpdateOldIndexes() async throws { + + var schema = createDummySchema() + schema.indexes = [ + "meta": ["world": "peace"], + "stop": ["being": "greedy"] + ] + schema.pendingIndexes.removeAll() + + let serverResponse = schema + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(serverResponse) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let saved = try await schema.update() + XCTAssertEqual(saved.fields, serverResponse.fields) + XCTAssertEqual(saved.indexes, serverResponse.indexes) + XCTAssertEqual(saved.classLevelPermissions, serverResponse.classLevelPermissions) + XCTAssertEqual(saved.className, serverResponse.className) + XCTAssertTrue(saved.pendingIndexes.isEmpty) + } + @MainActor func testUpdateError() async throws { diff --git a/Tests/ParseSwiftTests/ParseSchemaTests.swift b/Tests/ParseSwiftTests/ParseSchemaTests.swift index 678c7016d..096916b30 100644 --- a/Tests/ParseSwiftTests/ParseSchemaTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaTests.swift @@ -101,6 +101,12 @@ class ParseSchemaTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(schema.classLevelPermissions, clp) } + func testParseFieldOptionsEncode() { + let options = ParseFieldOptions(required: false, defauleValue: 2) + XCTAssertEqual(options.description, + "ParseFieldOptions ({\"defaultValue\":2,\"required\":false})") + } + func testSchemaEncode() throws { let schema = createDummySchema() // swiftlint:disable:next line_length From e746c0d58cae0424395a02e10c15e106fb49d53e Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 01:17:11 -0400 Subject: [PATCH 52/55] remove unused code --- Sources/ParseSwift/Types/ParseSchema.swift | 106 +++++++-------------- 1 file changed, 32 insertions(+), 74 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 6a86d73b1..1f62910e0 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -313,21 +313,10 @@ extension ParseSchema { var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try createCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } + createCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) } /** @@ -355,24 +344,13 @@ extension ParseSchema { var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try mutableSchema.updateCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } + mutableSchema.updateCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) } - func createCommand() throws -> API.Command { + func createCommand() -> API.Command { return API.Command(method: .POST, path: endpoint, @@ -381,7 +359,7 @@ extension ParseSchema { } } - func updateCommand() throws -> API.Command { + func updateCommand() -> API.Command { API.Command(method: .PUT, path: endpoint, @@ -417,28 +395,18 @@ extension ParseSchema { var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try purgeCommand().executeAsync(options: options, - callbackQueue: callbackQueue) { result in - switch result { - - case .success: - completion(.success(())) - case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } + purgeCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) } } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } - } catch { - callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) - } - } + } } /** @@ -465,31 +433,21 @@ extension ParseSchema { var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try deleteCommand().executeAsync(options: options, - callbackQueue: callbackQueue) { result in - switch result { - - case .success: - completion(.success(())) - case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } + deleteCommand().executeAsync(options: options, + callbackQueue: callbackQueue) { result in + switch result { + + case .success: + completion(.success(())) + case .failure(let error): + callbackQueue.async { + completion(.failure(error)) } } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } - } catch { - callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) - } - } + } } - func purgeCommand() throws -> API.Command { + func purgeCommand() -> API.Command { API.Command(method: .DELETE, path: endpointPurge) { (data) -> NoBody in @@ -502,7 +460,7 @@ extension ParseSchema { } } - func deleteCommand() throws -> API.Command { + func deleteCommand() -> API.Command { API.Command(method: .DELETE, path: endpoint, From b0eafde4a3649555c76b7f3873f9aa2ec5d4a26a Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 01:28:37 -0400 Subject: [PATCH 53/55] remove more code --- Sources/ParseSwift/Types/ParseSchema.swift | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index 1f62910e0..5dff34780 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -265,24 +265,13 @@ extension ParseSchema { var options = options options.insert(.useMasterKey) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { - try fetchCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - completion: completion) - } catch { - callbackQueue.async { - if let error = error as? ParseError { - completion(.failure(error)) - } else { - completion(.failure(ParseError(code: .unknownError, - message: error.localizedDescription))) - } - } - } + fetchCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + completion: completion) } - func fetchCommand() throws -> API.Command { + func fetchCommand() -> API.Command { return API.Command(method: .GET, path: endpoint) { (data) -> Self in From cd9bbd03496640f5a272a3c661410649c09d59b1 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 01:46:25 -0400 Subject: [PATCH 54/55] fix combine tests --- .../ParseSchemaCombineTests.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift index 86d426ae5..7bdfa4d31 100644 --- a/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseSchemaCombineTests.swift @@ -70,7 +70,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ .addIndex("hello", field: "world", index: "yolo") } - func testCreate() async throws { + func testCreate() throws { var subscriptions = Set() let schema = createDummySchema() @@ -108,7 +108,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testCreateError() async throws { + func testCreateError() throws { var subscriptions = Set() let schema = createDummySchema() @@ -141,7 +141,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testUpdate() async throws { + func testUpdate() throws { var subscriptions = Set() let schema = createDummySchema() @@ -179,7 +179,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testUpdateError() async throws { + func testUpdateError() throws { var subscriptions = Set() let schema = createDummySchema() @@ -212,7 +212,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testFetch() async throws { + func testFetch() throws { var subscriptions = Set() let schema = createDummySchema() @@ -250,7 +250,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testFetchError() async throws { + func testFetchError() throws { var subscriptions = Set() let schema = createDummySchema() @@ -283,7 +283,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testPurge() async throws { + func testPurge() throws { var subscriptions = Set() let schema = createDummySchema() @@ -316,7 +316,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testPurgeError() async throws { + func testPurgeError() throws { var subscriptions = Set() let schema = createDummySchema() @@ -349,7 +349,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testDelete() async throws { + func testDelete() throws { var subscriptions = Set() let schema = createDummySchema() @@ -382,7 +382,7 @@ class ParseSchemaCombineTests: XCTestCase { // swiftlint:disable:this type_body_ wait(for: [expectation1], timeout: 20.0) } - func testDeleteError() async throws { + func testDeleteError() throws { var subscriptions = Set() let schema = createDummySchema() From ae88e8e5497d9ca6c926d2baa2cb1d06239b17e0 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Mon, 30 May 2022 10:43:29 -0400 Subject: [PATCH 55/55] add userField to CLP --- .../Contents.swift | 33 +++++++--- Sources/ParseSwift/Types/ParseCLP.swift | 60 ++++++++++++++++++- Tests/ParseSwiftTests/ParseCLPTests.swift | 17 ++++++ 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift index ecbde8a3a..50b8595d0 100644 --- a/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift @@ -48,8 +48,10 @@ struct GameScore2: ParseObject { //: Your own properties. var points: Int? + var level: Int? var data: ParseBytes? var owner: User? + var rivals: [User]? //: Implement your own version of merge func merge(with object: Self) throws -> Self { @@ -58,6 +60,10 @@ struct GameScore2: ParseObject { original: object) { updated.points = object.points } + if updated.shouldRestoreKey(\.level, + original: object) { + updated.level = object.level + } if updated.shouldRestoreKey(\.data, original: object) { updated.data = object.data @@ -66,6 +72,10 @@ struct GameScore2: ParseObject { original: object) { updated.owner = object.owner } + if updated.shouldRestoreKey(\.rivals, + original: object) { + updated.rivals = object.rivals + } return updated } } @@ -93,14 +103,21 @@ var gameScoreSchema = ParseSchema(classLevelPermissions: clp) .addField("points", type: .number, options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("level", + type: .number, + options: ParseFieldOptions(required: false, defauleValue: nil)) .addField("data", type: .bytes, options: ParseFieldOptions(required: false, defauleValue: nil)) do { - gameScoreSchema = try gameScoreSchema.addField("owner", - type: .pointer, - options: ParseFieldOptions(required: false, defauleValue: nil)) + gameScoreSchema = try gameScoreSchema + .addField("owner", + type: .pointer, + options: ParseFieldOptions(required: false, defauleValue: nil)) + .addField("rivals", + type: .array, + options: ParseFieldOptions<[User]>(required: false, defauleValue: nil)) } catch { print("Can't add field: \(gameScoreSchema)") } @@ -120,7 +137,7 @@ let clp2 = clp.setPointerFields(Set(["owner"]), on: .get) gameScoreSchema.classLevelPermissions = clp2 //: In addition, we can add an index. -gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "points", index: 1) +gameScoreSchema = gameScoreSchema.addIndex("myIndex", field: "level", index: 1) //: Next, we need to update the schema on the server with the changes. gameScoreSchema.update { result in @@ -174,11 +191,13 @@ gameScoreSchema.update { result in } /*: - Fields can also be deleted on a schema. Lets remove - the **data** field since it's not going being used. + Sets of fields can also be protected from access. Lets protect + some fields from access. */ var clp3 = gameScoreSchema.classLevelPermissions -clp3 = clp3?.setProtectedFieldsPublic(["owner"]) +clp3 = clp3? + .setProtectedFieldsPublic(["owner"]) + .setProtectedFields(["level"], userField: "rivals") gameScoreSchema.classLevelPermissions = clp3 //: Next, we need to update the schema on the server with the changes. diff --git a/Sources/ParseSwift/Types/ParseCLP.swift b/Sources/ParseSwift/Types/ParseCLP.swift index 1dc84bf4a..7cc90afec 100644 --- a/Sources/ParseSwift/Types/ParseCLP.swift +++ b/Sources/ParseSwift/Types/ParseCLP.swift @@ -122,6 +122,10 @@ public struct ParseCLP: Codable, Equatable { // MARK: Default Implementation extension ParseCLP { + static func getUserFieldAccess(_ field: String) -> String { + "userField:\(field)" + } + func hasAccess(_ keyPath: KeyPath, for entity: String) -> Bool { self[keyPath: keyPath]?[entity]?.value as? Bool ?? false @@ -801,6 +805,17 @@ public extension ParseCLP { getProtectedFields(Access.requiresAuthentication.rawValue) } + /** + Get the protected fields either a field of `ParseUser` type or + an array of `ParseUser`'s in a Parse class cannot access. + - parameter field: A field in a Parse class that is either of `ParseUser` type or + an array of `ParseUser`'s. + - returns: The set protected fields that cannot be accessed. + */ + func getProtectedFieldsUser(_ field: String) -> Set { + getProtectedFields(Self.getUserFieldAccess(field)) + } + /** Get the protected fields the given `ParseUser` objectId cannot access. - parameter objectId: The `ParseUser` objectId access to check. @@ -862,10 +877,25 @@ public extension ParseCLP { setProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) } + /** + Set whether the given field that is either of `ParseUser` type or an array of `ParseUser`'s + should not have access to specific fields of a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter userField: A field in a Parse class that is either of `ParseUser` type or + an array of `ParseUser`'s to restrict access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + */ + func setProtectedFields(_ fields: Set, userField: String) -> Self { + setProtected(fields, + on: \.protectedFields, + for: Self.getUserFieldAccess(userField)) + } + /** Set whether the given `ParseUser` objectId should not have access to specific fields of a Parse class. - - parameter objectId: The `ParseUser` objectId to restrict access to. - parameter fields: The set of fields that should be protected from access. + - parameter objectId: The `ParseUser` objectId to restrict access to. - returns: A mutated instance of `ParseCLP` for easy chaining. - throws: An error of type `ParseError`. */ @@ -930,6 +960,20 @@ public extension ParseCLP { addProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) } + /** + Add to the set of specific fields the given field that is either of `ParseUser` type or an array of `ParseUser`'s + should not have access to on a Parse class. + - parameter fields: The set of fields that should be protected from access. + - parameter userField: A field in a Parse class that is either of `ParseUser` type or + an array of `ParseUser`'s to restrict access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method adds on to the current set of `fields` in the CLP. + */ + func addProtectedFieldsUser(_ fields: Set, userField: String) -> Self { + addProtected(fields, on: \.protectedFields, for: Self.getUserFieldAccess(userField)) + } + /** Add to the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. - parameter fields: The set of fields that should be protected from access. @@ -1003,6 +1047,20 @@ public extension ParseCLP { removeProtected(fields, on: \.protectedFields, for: Access.requiresAuthentication.rawValue) } + /** + Remove fields from the set of specific fields the given field that is either of `ParseUser` type + or an array of `ParseUser`'s should not have access to on a Parse class. + - parameter fields: The set of fields that should be removed from protected access. + - parameter userField: A field in a Parse class that is either of `ParseUser` type or + an array of `ParseUser`'s to restrict access to. + - returns: A mutated instance of `ParseCLP` for easy chaining. + - throws: An error of type `ParseError`. + - note: This method removes from the current set of `fields` in the CLP. + */ + func removeProtectedFieldsUser(_ fields: Set, userField: String) -> Self { + removeProtected(fields, on: \.protectedFields, for: Self.getUserFieldAccess(userField)) + } + /** Remove fields from the set of specific fields the given `ParseUser` objectId should not have access to on a Parse class. diff --git a/Tests/ParseSwiftTests/ParseCLPTests.swift b/Tests/ParseSwiftTests/ParseCLPTests.swift index 4ab37760e..1c2f9f5df 100644 --- a/Tests/ParseSwiftTests/ParseCLPTests.swift +++ b/Tests/ParseSwiftTests/ParseCLPTests.swift @@ -210,6 +210,23 @@ class ParseCLPTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(clp4.getProtectedFieldsRequiresAuthentication(), newField) } + func testProtectedFieldsUserField() throws { + let fields = Set(["hello", "world"]) + let userField = "peace" + let clp = ParseCLP().setProtectedFields(fields, userField: userField) + XCTAssertEqual(clp.getProtectedFieldsUser(userField), fields) + + let newField = Set(["new"]) + let clp2 = clp.addProtectedFieldsUser(newField, userField: userField) + XCTAssertEqual(clp2.getProtectedFieldsUser(userField), fields.union(newField)) + + let clp3 = clp2.removeProtectedFieldsUser(newField, userField: userField) + XCTAssertEqual(clp3.getProtectedFieldsUser(userField), fields) + + let clp4 = ParseCLP().addProtectedFieldsUser(newField, userField: userField) + XCTAssertEqual(clp4.getProtectedFieldsUser(userField), newField) + } + func testProtectedFieldsObjectId() throws { let fields = Set(["hello", "world"]) let clp = ParseCLP().setProtectedFields(fields, for: objectId)