Skip to content

Commit

Permalink
Merge branch 'main' into addOTP
Browse files Browse the repository at this point in the history
  • Loading branch information
cbaker6 authored Jul 14, 2024
2 parents f547717 + f13ad6b commit 50df3e1
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 72 deletions.
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,26 @@
# Parse-Swift Changelog

### main
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.10.0...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.10.3...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
* _Contributing to this repo? Add info about your change here to be included in the next release_

### 5.10.3
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.10.2...5.10.3), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.10.3/documentation/parseswift)

__Fixes__
* Allow encoding of id and className on nested types that are not ParseObjects ([#176](https://github.com/netreconlab/Parse-Swift/pull/177)), thanks to [Corey Baker](https://github.com/cbaker6).

### 5.10.2
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.10.1...5.10.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.10.2/documentation/parseswift)

__Fixes__
* Improve ParseObject conformance to Hashable to prevent collision attacks ([#176](https://github.com/netreconlab/Parse-Swift/pull/176)), thanks to [Corey Baker](https://github.com/cbaker6).

### 5.10.1
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.10.0...5.10.1), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.10.1/documentation/parseswift)

__Fixes__
* Make ParseEncoder sendable ([#175](https://github.com/netreconlab/Parse-Swift/pull/175)), thanks to [Corey Baker](https://github.com/cbaker6).

### 5.10.0
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.9.3...5.10.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.10.0/documentation/parseswift)
Expand Down
21 changes: 12 additions & 9 deletions Sources/ParseSwift/Coding/ParseEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ extension Dictionary: _JSONStringDictionaryEncodableMarker where Key == String,
ParseEncoder matches the features of the [Swift 5.4 JSONEncoder ](https://github.com/apple/swift/blob/main/stdlib/public/Darwin/Foundation/JSONEncoder.swift).
Update commits as needed for improvement.
*/
public struct ParseEncoder {
public struct ParseEncoder: Sendable {
let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy?
let outputFormatting: JSONEncoder.OutputFormatting?

Expand All @@ -72,14 +72,17 @@ public struct ParseEncoder {
case custom(Set<String>)

func keys() -> Set<String> {
let defaultObjectKeys = Set(["createdAt",
"updatedAt",
"objectId",
"className",
"emailVerified",
"id",
"score",
"originalData"])
let defaultObjectKeys = Set(
[
"objectId",
"createdAt",
"updatedAt",
"emailVerified",
"score",
"originalData"
]
)

switch self {

case .object:
Expand Down
25 changes: 10 additions & 15 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,6 @@ public protocol ParseObject: ParseTypeable,
// MARK: Default Implementations
public extension ParseObject {

/**
A computed property that is a unique identifier and makes it easy to use `ParseObject`'s
as models in MVVM and SwiftUI.
- note: `id` allows `ParseObject`'s to be used even if they have not been saved and/or missing an `objectId`.
- important: `id` will have the same value as `objectId` when a `ParseObject` contains an `objectId`.
*/
var id: String {
objectId ?? UUID().uuidString
}

var mergeable: Self {
guard isSaved,
originalData == nil else {
Expand Down Expand Up @@ -245,13 +235,18 @@ extension ParseObject {
}
}

// MARK: Hashable
// MARK: Identifiable
public extension ParseObject {
func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
hasher.combine(createdAt)
hasher.combine(updatedAt)

/**
A computed property that ensures `ParseObject`'s can be uniquely identified across instances.
- note: `id` allows `ParseObject`'s to be uniquely identified even if they have not been saved and/or missing an `objectId`.
- important: `id` will have the same value as `objectId` when a `ParseObject` contains an `objectId`.
*/
var id: String {
objectId ?? UUID().uuidString
}

}

// MARK: Helper Methods
Expand Down
7 changes: 0 additions & 7 deletions Sources/ParseSwift/Objects/ParseRole.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,6 @@ public extension ParseRole {
self.ACL = acl
}

func hash(into hasher: inout Hasher) {
let name = self.name ?? self.objectId
hasher.combine(name)
hasher.combine(createdAt)
hasher.combine(updatedAt)
}

func mergeParse(with object: Self) throws -> Self {
guard hasSameObjectId(as: object) else {
throw ParseError(code: .otherCause,
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/ParseConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

enum ParseConstants {
static let sdk = "swift"
static let version = "5.10.0"
static let version = "5.10.3"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
8 changes: 0 additions & 8 deletions Sources/ParseSwift/Protocols/Fileable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,4 @@ extension Fileable {
var isSaved: Bool {
return url != nil
}

public static func == (lhs: Self, rhs: Self) -> Bool {
guard let lURL = lhs.url,
let rURL = rhs.url else {
return lhs.id == rhs.id
}
return lURL == rURL
}
}
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Protocols/ParseHookParametable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ import Foundation
Conforming to `ParseHookParametable` allows types that can be created
to decode parameters in `ParseHookFunctionRequest`'s.
*/
public protocol ParseHookParametable: Codable, Equatable, Sendable {}
public protocol ParseHookParametable: ParseTypeable {}
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Protocols/ParseTypeable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Foundation
*/
public protocol ParseTypeable: Codable,
Sendable,
Equatable,
Hashable,
CustomDebugStringConvertible,
CustomStringConvertible {}

Expand Down
11 changes: 11 additions & 0 deletions Sources/ParseSwift/Types/ParseError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,17 @@ extension ParseError: LocalizedError {
}
}

// MARK: Hashable
extension ParseError {
public func hash(into hasher: inout Hasher) {
hasher.combine(code)
hasher.combine(message)
hasher.combine(error)
hasher.combine(otherCode)
hasher.combine(swift?.localizedDescription)
}
}

// MARK: Equatable
extension ParseError: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
Expand Down
50 changes: 24 additions & 26 deletions Sources/ParseSwift/Types/ParseFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,6 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable, Identifiable {
&& data == nil
}

/**
A computed property that is a unique identifier and makes it easy to use `ParseFile`'s
as models in MVVM and SwiftUI.
- note: `id` allows `ParseFile`'s to be used even when they are not saved.
- important: `id` will have the same value as `name` when a `ParseFile` is saved.
*/
public var id: String {
guard isSaved else {
guard let cloudURL = cloudURL else {
guard let localURL = localURL else {
guard let data = data else {
return name
}
return "\(name)_\(data)"
}
return combineName(with: localURL)
}
return combineName(with: cloudURL)
}
return name
}

/**
The name of the file.
Before the file is saved, this is the filename given by the user.
Expand Down Expand Up @@ -159,10 +137,6 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable, Identifiable {
self.options = options
}

public func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}

public func isSaved() async throws -> Bool {
isSaved
}
Expand All @@ -174,6 +148,30 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable, Identifiable {
}
}

// MARK: Identifiable
extension ParseFile {
/**
A computed property that ensures `ParseFile`'s can be uniquely identified across instances.
- note: `id` allows `ParseFile`'s to be uniquely identified even if they have not been saved.
- important: `id` will have the same value as `objectId` when a `ParseObject` contains an `objectId`.
*/
public var id: String {
guard isSaved else {
guard let cloudURL = cloudURL else {
guard let localURL = localURL else {
guard let data = data else {
return name
}
return "\(name)_\(data)"
}
return combineName(with: localURL)
}
return combineName(with: cloudURL)
}
return name
}
}

// MARK: Helper Methods (internal)
extension ParseFile {
func combineName(with url: URL) -> String {
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Types/ParseHookResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Foundation
Build a response after processing a `ParseHookFunctionRequest`
or `ParseHookTriggerRequest`.
*/
public struct ParseHookResponse<R: Codable & Equatable & Sendable>: ParseTypeable {
public struct ParseHookResponse<R: Codable & Hashable & Sendable>: ParseTypeable {
/// The data to return in the response.
public var success: R?
/// An object with a Parse code and message.
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Types/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public struct Query<T>: ParseTypeable where T: ParseObject {
- parameter key: The key to order by.
*/
public enum Order: Codable, Equatable, Sendable {
public enum Order: ParseTypeable {
/// Sort in ascending order based on `key`.
case ascending(String)
/// Sort in descending order based on `key`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import XCTest
@testable import ParseSwift

class ParseEncoderTests: XCTestCase {

struct Dummy: Codable, Hashable {
var id: String
}

struct GameScore: ParseObject, ParseQueryScorable {
// These are required by ParseObject
var objectId: String?
Expand All @@ -24,6 +29,7 @@ class ParseEncoderTests: XCTestCase {

// Your own properties
var points: Int
var dummy: Dummy?

// a custom initializer
init() {
Expand Down
17 changes: 17 additions & 0 deletions Tests/ParseSwiftTests/ParseErrorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,23 @@ class ParseErrorTests: XCTestCase {
XCTAssertNil(error.containedIn([.operationForbidden, .invalidQuery]))
}

func testHashing() throws {
let error1 = ParseError(code: .accountAlreadyLinked, message: "Hello")
let error2 = ParseError(code: .accountAlreadyLinked, message: "World")
let error3 = error1

var setOfSameErrors = Set([error1, error1, error3])
XCTAssertEqual(setOfSameErrors.count, 1)
XCTAssertEqual(setOfSameErrors.first, error1)
XCTAssertEqual(setOfSameErrors.first, error3)
XCTAssertNotEqual(setOfSameErrors.first, error2)
setOfSameErrors.insert(error2)
XCTAssertEqual(setOfSameErrors.count, 2)
XCTAssertTrue(setOfSameErrors.contains(error1))
XCTAssertTrue(setOfSameErrors.contains(error2))
XCTAssertTrue(setOfSameErrors.contains(error3))
}

func testErrorCount() throws {
let errorCodes = ParseError.Code.allCases
XCTAssertGreaterThan(errorCodes.count, 50)
Expand Down
2 changes: 1 addition & 1 deletion Tests/ParseSwiftTests/ParseObjectAsyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1801,7 +1801,7 @@ class ParseObjectAsyncTests: XCTestCase { // swiftlint:disable:this type_body_le
XCTAssertEqual(savedGame.objectId, gameOnServer.objectId)
XCTAssertEqual(savedGame.createdAt, gameOnServer.createdAt)
XCTAssertEqual(savedGame.updatedAt, gameOnServer.createdAt)
XCTAssertEqual(savedGame.profilePicture, gameOnServer.profilePicture)
XCTAssertEqual(savedGame.profilePicture?.url, gameOnServer.profilePicture?.url)
}
#endif
}
29 changes: 29 additions & 0 deletions Tests/ParseSwiftTests/ParseObjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import XCTest
@testable import ParseSwift

class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length

struct Dummy: Codable, Hashable {
var id: String
}

struct Level: ParseObject {
var objectId: String?

Expand Down Expand Up @@ -43,6 +48,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length
var level: Level?
var levels: [Level]?
var nextLevel: Level?
var dummy: Dummy?

//: custom initializers
init() {}
Expand Down Expand Up @@ -348,6 +354,29 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length
XCTAssertTrue(score1.shouldRestoreKey(\.nextLevel, original: score2))
}

func testParseEncoderAllowsIdOnNestedTypesOnParseObject() throws {
var score = GameScore(points: 5)
score.dummy = Dummy(id: "hello")

let object = try ParseCoding
.parseEncoder()
.encode(
score,
acl: nil,
collectChildren: true,
objectsSavedBeforeThisOne: nil,
filesSavedBeforeThisOne: nil
)
let decoded = String(
decoding: object.encoded,
as: UTF8.self
)
XCTAssertEqual(
decoded,
#"{"dummy":{"id":"hello"},"player":"Jen","points":5}"#
)
}

func testParseObjectMutable() throws {
var score = GameScore(points: 19, name: "fire")
XCTAssertEqual(score, score.mergeable)
Expand Down

0 comments on commit 50df3e1

Please sign in to comment.