From 6e81c2fdd05ace300fa2c2531c9fbebc1ca083d6 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 15 Mar 2023 20:59:47 -0400 Subject: [PATCH] fix: All object endpoints need to be public (#80) * fix: All object endpoints need to be public * add public endpoint tests * add changelog --- CHANGELOG.md | 3 + ParseSwift.xcodeproj/project.pbxproj | 8 ++ Sources/ParseSwift/API/API.swift | 2 +- .../Objects/ParseInstallation.swift | 14 +- Sources/ParseSwift/Objects/ParseRole.swift | 13 +- Sources/ParseSwift/Objects/ParseSession.swift | 3 - Sources/ParseSwift/Objects/ParseUser.swift | 17 ++- .../ParseSwiftTests/ObjectEndpointTests.swift | 123 ++++++++++++++++++ 8 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 Tests/ParseSwiftTests/ObjectEndpointTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2284b9b..146168f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ __New features__ * Add ParseUser.loginAs(objectId: String) method to allow impersonating a user. This method requires the server primaryKey and is intended to run server-side ([#79](https://github.com/netreconlab/Parse-Swift/pull/79)), thanks to [Corey Baker](https://github.com/cbaker6). +__Fixes__ +* Fix ParseUser.become(), ParseUser.linkCommand, and other calls that depend on a specific endpoint for specialized ParseObjects such as ParseUser, ParseInstallation, ParseRole, etc. ([#80](https://github.com/netreconlab/Parse-Swift/pull/80)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 5.2.0 [Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.1.1...5.2.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.2.0/documentation/parseswift) diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index baf6c2852..c63a866d9 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -462,6 +462,9 @@ 70732C5A2606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70732C592606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift */; }; 70732C5B2606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70732C592606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift */; }; 70732C5C2606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70732C592606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift */; }; + 7073842B29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7073842A29C2910E00E21055 /* ObjectEndpointTests.swift */; }; + 7073842C29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7073842A29C2910E00E21055 /* ObjectEndpointTests.swift */; }; + 7073842D29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7073842A29C2910E00E21055 /* ObjectEndpointTests.swift */; }; 707A3BF125B0A4F0000D215C /* ParseAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 707A3BF025B0A4F0000D215C /* ParseAuthentication.swift */; }; 707A3BF225B0A4F0000D215C /* ParseAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 707A3BF025B0A4F0000D215C /* ParseAuthentication.swift */; }; 707A3BF325B0A4F0000D215C /* ParseAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 707A3BF025B0A4F0000D215C /* ParseAuthentication.swift */; }; @@ -1308,6 +1311,7 @@ 706436A827341FD0007C6461 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 70647E9B259E3A9A004C1004 /* ParseEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseEncodable.swift; sourceTree = ""; }; 70732C592606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseObjectCustomObjectIdTests.swift; sourceTree = ""; }; + 7073842A29C2910E00E21055 /* ObjectEndpointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectEndpointTests.swift; sourceTree = ""; }; 707A3BF025B0A4F0000D215C /* ParseAuthentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAuthentication.swift; sourceTree = ""; }; 707A3C1025B0A8E8000D215C /* ParseAnonymous.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAnonymous.swift; sourceTree = ""; }; 707A3C1F25B14BCF000D215C /* ParseApple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseApple.swift; sourceTree = ""; }; @@ -1625,6 +1629,7 @@ 4AA8076E1F794C1C008CD551 /* KeychainStoreTests.swift */, 70D41D6A28B294C100613510 /* MigrateObjCSDKCombineTests.swift */, 70D41D6628B0235100613510 /* MigrateObjCSDKTests.swift */, + 7073842A29C2910E00E21055 /* ObjectEndpointTests.swift */, 9194657724F16E330070296B /* ParseACLTests.swift */, 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */, 91CB9536265966DF0043E5D6 /* ParseAnalyticsCombineTests.swift */, @@ -2973,6 +2978,7 @@ 70C7DC2224D20F190050419B /* ParseObjectBatchTests.swift in Sources */, 7044C1BB25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45A2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, + 7073842B29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */, 7FFF552F2217E72A007C3B4E /* AnyCodableTests.swift in Sources */, 70212D2D2855266400386163 /* ParsePushAsyncTests.swift in Sources */, 70F03A5E2780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, @@ -3299,6 +3305,7 @@ 709B984E2556ECAA00507778 /* ParseGeoPointTests.swift in Sources */, 7044C1BD25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45C2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, + 7073842D29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */, 709B984B2556ECAA00507778 /* MockURLProtocol.swift in Sources */, 70212D352855266600386163 /* ParsePushAsyncTests.swift in Sources */, 70F03A602780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, @@ -3418,6 +3425,7 @@ 70F2E2C0254F283000B2EA5C /* MockURLResponse.swift in Sources */, 7044C1BC25C52E410011F6E7 /* ParseInstallationCombineTests.swift in Sources */, 917BA45B2703FD2200F8D747 /* ParseAuthenticationAsyncTests.swift in Sources */, + 7073842C29C2910E00E21055 /* ObjectEndpointTests.swift in Sources */, 70F2E2BE254F283000B2EA5C /* ParseObjectBatchTests.swift in Sources */, 70212D312855266500386163 /* ParsePushAsyncTests.swift in Sources */, 70F03A5F2780EAC700E5AFB4 /* ParseGitHubTests.swift in Sources */, diff --git a/Sources/ParseSwift/API/API.swift b/Sources/ParseSwift/API/API.swift index 5197ce18e..edfafa246 100644 --- a/Sources/ParseSwift/API/API.swift +++ b/Sources/ParseSwift/API/API.swift @@ -55,7 +55,7 @@ public struct API { case hookTrigger(request: TriggerRequest) case any(String) - var urlComponent: String { + public var urlComponent: String { switch self { case .batch: return "/batch" diff --git a/Sources/ParseSwift/Objects/ParseInstallation.swift b/Sources/ParseSwift/Objects/ParseInstallation.swift index 5bfdd8a9d..8646a8aee 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation.swift @@ -95,6 +95,13 @@ public extension ParseInstallation { "_Installation" } + var endpoint: API.Endpoint { + if let objectId = objectId { + return .installation(objectId: objectId) + } + return .installations + } + // swiftlint:disable:next cyclomatic_complexity function_body_length func mergeParse(with object: Self) throws -> Self { guard hasSameObjectId(as: object) else { @@ -164,13 +171,6 @@ public extension ParseInstallation { // MARK: Convenience extension ParseInstallation { - var endpoint: API.Endpoint { - if let objectId = objectId { - return .installation(objectId: objectId) - } - - return .installations - } func endpoint(_ method: API.Method) -> API.Endpoint { if !Parse.configuration.isRequiringCustomObjectIds || method != .POST { diff --git a/Sources/ParseSwift/Objects/ParseRole.swift b/Sources/ParseSwift/Objects/ParseRole.swift index f1465c0f6..6daf5bc6a 100644 --- a/Sources/ParseSwift/Objects/ParseRole.swift +++ b/Sources/ParseSwift/Objects/ParseRole.swift @@ -76,6 +76,13 @@ public extension ParseRole { "_Role" } + var endpoint: API.Endpoint { + if let objectId = objectId { + return .role(objectId: objectId) + } + return .roles + } + var users: ParseRelation? { try? ParseRelation(parent: self, key: "users", className: RoleUser.className) } @@ -130,12 +137,6 @@ public extension ParseRole { // MARK: Convenience extension ParseRole { - var endpoint: API.Endpoint { - if let objectId = objectId { - return .role(objectId: objectId) - } - return .roles - } static func checkName(_ name: String) throws { // swiftlint:disable:next line_length diff --git a/Sources/ParseSwift/Objects/ParseSession.swift b/Sources/ParseSwift/Objects/ParseSession.swift index e49dfcb35..20cfa38b2 100644 --- a/Sources/ParseSwift/Objects/ParseSession.swift +++ b/Sources/ParseSwift/Objects/ParseSession.swift @@ -43,10 +43,7 @@ public extension ParseSession { static var className: String { "_Session" } -} -// MARK: Convenience -extension ParseSession { var endpoint: API.Endpoint { if let objectId = objectId { return .session(objectId: objectId) diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index 53367b4bf..57796a84e 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -44,6 +44,13 @@ public extension ParseUser { "_User" } + var endpoint: API.Endpoint { + if let objectId = objectId { + return .user(objectId: objectId) + } + return .users + } + func mergeParse(with object: Self) throws -> Self { guard hasSameObjectId(as: object) else { throw ParseError(code: .otherCause, @@ -94,13 +101,6 @@ public extension ParseUser { // MARK: Convenience extension ParseUser { - var endpoint: API.Endpoint { - if let objectId = objectId { - return .user(objectId: objectId) - } - - return .users - } func endpoint(_ method: API.Method) -> API.Endpoint { if !Parse.configuration.isRequiringCustomObjectIds || method != .POST { @@ -445,9 +445,8 @@ extension ParseUser { #endif internal func meCommand(sessionToken: String) throws -> API.Command { - // BAKER: path endpoint isn't working here for some reason return API.Command(method: .GET, - path: .user(objectId: "me")) { (data) async throws -> Self in + path: endpoint) { (data) async throws -> Self in let user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) if let current = try? await Self.current() { diff --git a/Tests/ParseSwiftTests/ObjectEndpointTests.swift b/Tests/ParseSwiftTests/ObjectEndpointTests.swift new file mode 100644 index 000000000..c358d35ce --- /dev/null +++ b/Tests/ParseSwiftTests/ObjectEndpointTests.swift @@ -0,0 +1,123 @@ +// +// ObjectEndpointTests.swift +// ParseSwift +// +// Created by Corey Baker on 3/15/23. +// Copyright © 2023 Network Reconnaissance Lab. All rights reserved. +// + +import Foundation +import XCTest +import ParseSwift + +class ObjectEndpointTests: XCTestCase { + 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? + } + + struct Installation: ParseInstallation { + var installationId: String? + var deviceType: String? + var deviceToken: String? + var badge: Int? + var timeZone: String? + var channels: [String]? + var appName: String? + var appIdentifier: String? + var appVersion: String? + var parseVersion: String? + var localeIdentifier: String? + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + var originalData: Data? + var customKey: String? + } + + struct Session: ParseSession { + + var sessionToken: String + var user: User + var restricted: Bool? + var createdWith: [String: String] + var installationId: String + var expiresAt: Date + var originalData: Data? + + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ParseACL? + + init() { + sessionToken = "hello" + user = User() + restricted = false + createdWith = ["yolo": "yaw"] + installationId = "yes" + expiresAt = Date() + } + } + + 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? + } + + func testUser() async throws { + let objectId = "yarr" + var user = User() + + XCTAssertEqual(user.endpoint.urlComponent, "/users") + user.objectId = objectId + XCTAssertEqual(user.endpoint.urlComponent, "/users/\(objectId)") + } + + func testInstallation() async throws { + let objectId = "yarr" + var installation = Installation() + + XCTAssertEqual(installation.endpoint.urlComponent, "/installations") + installation.objectId = objectId + XCTAssertEqual(installation.endpoint.urlComponent, "/installations/\(objectId)") + } + + func testSession() throws { + var session = Session() + XCTAssertEqual(session.endpoint.urlComponent, "/sessions") + session.objectId = "me" + XCTAssertEqual(session.endpoint.urlComponent, "/sessions/me") + } + + func testRole() throws { + var role = try Role(name: "Administrator") + XCTAssertEqual(role.endpoint.urlComponent, "/roles") + role.objectId = "me" + XCTAssertEqual(role.endpoint.urlComponent, "/roles/me") + } +}