Skip to content

Commit

Permalink
feat: Add concrete types ParseHookTrigger and ParseHookFunction (#122)
Browse files Browse the repository at this point in the history
* feat: Add concrete types ParseHookTrigger and ParseHookFunction

* nit

* coverage

* lower yield time of SDK init

* more coverage

* fix testcases

* improve docs and codecov

* fix mergeAutomatically() error

* Update changelog message
  • Loading branch information
cbaker6 authored Jun 24, 2023
1 parent 835083e commit 66627b5
Show file tree
Hide file tree
Showing 28 changed files with 565 additions and 227 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@
# Parse-Swift Changelog

### main
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.7.4...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.8.0...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.8.0
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.7.4...5.8.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.8.0/documentation/parseswift)

__New features__
* Add concrete types: ParseHookTrigger and ParseHookFunction to reduce code developers need to write. Deprecate triggerName in favor of trigger where possible. The SDK also yields for 0.5 second as oppose to 1 second, which can lead to faster app starts ([#122](https://github.com/netreconlab/Parse-Swift/pull/122)), thanks to [Corey Baker](https://github.com/cbaker6).

__Fixes__
* There was a problem when the developer did not implement merge() on a ParseObject and depended on the internal mergeAutomatically() to merge. If the developer used mergeAutomatically() indirectly for objects with different objectId's, the method allowed the merge. Now the method now throws the proper error ([#122](https://github.com/netreconlab/Parse-Swift/pull/122)), thanks to [Corey Baker](https://github.com/cbaker6).

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ Task {
Task {
let score1 = GameScore(points: 53)
//: You can also do
// let specificRelation = try await User.current().relation("scores", className: "GameScore")
// let specificRelation = try await User.current().relation("scores", object: GameScore.self)
do {
let currentUser = try await User.current()
let specificRelation = try currentUser.relation("scores", child: score1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,12 @@ Task {
}
}

/*:
Parse Hook Functions can be created by conforming to
`ParseHookFunctionable`.
*/
struct HookFunction: ParseHookFunctionable {
var functionName: String?
var url: URL?
}

/*:
Lets create our first Hook function by first creating an instance
with the name of the function and url for the hook.
*/
var myFunction = HookFunction(name: "foo",
url: URL(string: "https://api.example.com/foo"))
var myFunction = ParseHookFunction(name: "foo",
url: URL(string: "https://api.example.com/foo"))

//: Then, create the function on the server.
myFunction.create { result in
Expand Down Expand Up @@ -101,7 +92,7 @@ myFunction.delete { result in
You can also use the fetchAll type method to fetch all of
the current Hook functions.
*/
HookFunction.fetchAll { result in
ParseHookFunction.fetchAll { result in
switch result {
case .success(let functions):
print("Current: \"\(functions)\"")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,14 @@ struct GameScore: ParseObject {
}
}

/*:
Parse Hook Triggers can be created by conforming to
`ParseHookFunctionable`.
*/
struct HookTrigger: ParseHookTriggerable {
var className: String?
var triggerName: ParseHookTriggerType?
var url: URL?
}

/*:
Lets create our first Hook trigger by first creating an instance
with the name of the trigger and url for the hook.
*/
let gameScore = GameScore()
var myTrigger = HookTrigger(object: gameScore,
triggerName: .afterSave,
url: URL(string: "https://api.example.com/bar")!)
var myTrigger = ParseHookTrigger(object: gameScore,
trigger: .afterSave,
url: URL(string: "https://api.example.com/bar")!)

//: Then, create the trigger on the server.
myTrigger.create { result in
Expand Down Expand Up @@ -130,7 +120,7 @@ myTrigger.delete { result in
You can also use the fetchAll type method to fetch all of
the current Hook triggers.
*/
HookTrigger.fetchAll { result in
ParseHookTrigger.fetchAll { result in
switch result {
case .success(let triggers):
print("Current: \"\(triggers)\"")
Expand Down
8 changes: 8 additions & 0 deletions ParseSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
7030E09029BBBFFA0021970D /* ParseConfigCodable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7030E08F29BBBFFA0021970D /* ParseConfigCodable+combine.swift */; };
7030E09529BBC5740021970D /* ParseConfigCodableCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7030E09429BBC5740021970D /* ParseConfigCodableCombineTests.swift */; };
7031F356296F553200E077CC /* APICommandMultipleAttemptsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7031F355296F553200E077CC /* APICommandMultipleAttemptsTests.swift */; };
7034B9FD2A46380B00395CBC /* ParseHookTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7034B9FC2A46380B00395CBC /* ParseHookTrigger.swift */; };
7034B9FF2A46391200395CBC /* ParseHookFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7034B9FE2A46391200395CBC /* ParseHookFunction.swift */; };
7037DAB226384DE1005D7E62 /* TestParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7037DAB126384DE1005D7E62 /* TestParseEncoder.swift */; };
70385E6428563FD10084D306 /* ParsePushPayloadFirebaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70385E6328563FD10084D306 /* ParsePushPayloadFirebaseTests.swift */; };
70385E68285640A30084D306 /* ParsePushPayloadAnyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70385E67285640A30084D306 /* ParsePushPayloadAnyTests.swift */; };
Expand Down Expand Up @@ -394,6 +396,8 @@
7030E08F29BBBFFA0021970D /* ParseConfigCodable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseConfigCodable+combine.swift"; sourceTree = "<group>"; };
7030E09429BBC5740021970D /* ParseConfigCodableCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigCodableCombineTests.swift; sourceTree = "<group>"; };
7031F355296F553200E077CC /* APICommandMultipleAttemptsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICommandMultipleAttemptsTests.swift; sourceTree = "<group>"; };
7034B9FC2A46380B00395CBC /* ParseHookTrigger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseHookTrigger.swift; sourceTree = "<group>"; };
7034B9FE2A46391200395CBC /* ParseHookFunction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseHookFunction.swift; sourceTree = "<group>"; };
7037DAB126384DE1005D7E62 /* TestParseEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestParseEncoder.swift; sourceTree = "<group>"; };
70385E6328563FD10084D306 /* ParsePushPayloadFirebaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsePushPayloadFirebaseTests.swift; sourceTree = "<group>"; };
70385E67285640A30084D306 /* ParsePushPayloadAnyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsePushPayloadAnyTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1226,8 +1230,10 @@
7044C19025C4F5B60011F6E7 /* ParseFile+combine.swift */,
704E781B28CFFAF80075F952 /* ParseFileDefaultTransfer.swift */,
F97B45BC24D9C6F200F4A88B /* ParseGeoPoint.swift */,
7034B9FE2A46391200395CBC /* ParseHookFunction.swift */,
70385E7F2858EAA90084D306 /* ParseHookFunctionRequest.swift */,
70CE0A9328590A0A00DAEA86 /* ParseHookResponse.swift */,
7034B9FC2A46380B00395CBC /* ParseHookTrigger.swift */,
70CE0AB1285963A300DAEA86 /* ParseHookTriggerObjectRequest.swift */,
70B412B329801AFB00F706EA /* ParseHookTriggerRequest.swift */,
70D41D7F28B520E200613510 /* ParseKeychainAccessGroup.swift */,
Expand Down Expand Up @@ -1530,6 +1536,7 @@
70385E712858D2DD0084D306 /* ParseHookTriggerable.swift in Sources */,
91679D64268E596300F71809 /* ParseVersion.swift in Sources */,
91285B1C26990D7F0051B544 /* ParsePolygon.swift in Sources */,
7034B9FD2A46380B00395CBC /* ParseHookTrigger.swift in Sources */,
91BB8FCA2690AC99005A6BA5 /* QueryViewModel.swift in Sources */,
70B412B429801AFB00F706EA /* ParseHookTriggerRequest.swift in Sources */,
7085DD9426CBF3A70033B977 /* Documentation.docc in Sources */,
Expand Down Expand Up @@ -1595,6 +1602,7 @@
F97B465A24D9C78C00F4A88B /* ParseOperationIncrement.swift in Sources */,
7045769326BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */,
7C55F9E72860CD6B002A352D /* ParseSpotify.swift in Sources */,
7034B9FF2A46391200395CBC /* ParseHookFunction.swift in Sources */,
7003960925A184EF0052CB31 /* ParseLiveQuery.swift in Sources */,
7044C17525C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */,
705025B32845C302008D6624 /* ParsePushStatus.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/API/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public struct API {
case .hookTriggers:
return "/hooks/triggers/"
case .hookTrigger(let request):
return "/hooks/triggers/\(request.className)/\(request.triggerName)"
return "/hooks/triggers/\(request.className)/\(request.trigger)"
case .serverInfo:
return "/serverInfo"
case .any(let path):
Expand Down
4 changes: 4 additions & 0 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ public extension ParseObject {
extension ParseObject {

func mergeAutomatically(_ originalObject: Self) throws -> Self {
guard hasSameObjectId(as: originalObject) else {
throw ParseError(code: .otherCause,
message: "objectId's of objects do not match")
}
let updatedEncoded = try ParseCoding.jsonEncoder().encode(self)
let originalData = try ParseCoding.jsonEncoder().encode(originalObject)
guard let updated = try JSONSerialization.jsonObject(with: updatedEncoded) as? [String: AnyObject],
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseRole.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public extension ParseRole {
}

var users: ParseRelation<Self>? {
try? ParseRelation(parent: self, key: "users", className: RoleUser.className)
try? ParseRelation(parent: self, key: "users", object: RoleUser.self)
}

var roles: ParseRelation<Self>? {
try? ParseRelation(parent: self, key: "roles", className: Self.className)
try? ParseRelation(parent: self, key: "roles", object: Self.self)
}

init(name: String) throws {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ internal func initialize(applicationId: String,

internal func yieldIfNotInitialized(_ iteration: Int = 0) async throws {
guard ParseConfiguration.checkIfConfigured() else {
guard iteration < 5 else {
guard iteration < 10 else {
throw ParseError(code: .otherCause,
message: "The SDK needs to be initialized")
}
let nanoSeconds = UInt64(1 * 1_000_000_000)
let nanoSeconds = UInt64(1 * 500_000_000)
try await Task.sleep(nanoseconds: nanoSeconds)
try await yieldIfNotInitialized(iteration + 1)
return
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.7.4"
static let version = "5.8.0"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
8 changes: 7 additions & 1 deletion Sources/ParseSwift/Protocols/ParseHookFunctionable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ public extension ParseHookFunctionable {
}
}

/// A type of request for Parse Hook Functions.
public struct FunctionRequest: Encodable {
let functionName: String
let url: URL?

init<F>(hookFunction: F) throws where F: ParseHookFunctionable {
/**
Creates an instance.
- parameter hookFunction: A type that conforms to `ParseHookFunctionable`.
- throws: An error of `ParseError` type.
*/
public init<F>(hookFunction: F) throws where F: ParseHookFunctionable {
guard let functionName = hookFunction.functionName else {
throw ParseError(code: .otherCause,
message: "The \"functionName\" needs to be set: \(hookFunction)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation
exposed to the public.
*/
public protocol ParseHookTriggerRequestable: ParseHookRequestable {
/// The types of Parse Hook Trigger.
/// The name of the Parse Hook Trigger.
var triggerName: String? { get }
/// The number of clients connected.
var clients: Int? { get }
Expand Down
78 changes: 67 additions & 11 deletions Sources/ParseSwift/Protocols/ParseHookTriggerable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,70 @@ public protocol ParseHookTriggerable: ParseHookable {

// MARK: Default Implementation
public extension ParseHookTriggerable {

/**
Creates a new Parse hook trigger.
- parameter className: The name of the `ParseObject` the trigger should act on.
- parameter triggerName: The `ParseHookTriggerType` type.
- parameter trigger: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
init(className: String, triggerName: ParseHookTriggerType, url: URL) {
init(className: String, trigger: ParseHookTriggerType, url: URL) {
self.init()
self.className = className
self.triggerName = triggerName
self.triggerName = trigger
self.url = url
}

/**
Creates a new Parse hook trigger.
- parameter className: The name of the `ParseObject` the trigger should act on.
- parameter triggerName: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
@available(*, deprecated, message: "Change \"triggerName\" to \"trigger\"")
init(className: String, triggerName: ParseHookTriggerType, url: URL) {
self.init(className: className, trigger: triggerName, url: url)
}

/**
Creates a new Parse hook trigger.
- parameter object: The `ParseObject` the trigger should act on.
- parameter trigger: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
init<T>(object: T.Type, trigger: ParseHookTriggerType, url: URL) where T: ParseObject {
self.init(className: object.className, trigger: trigger, url: url)
}

/**
Creates a new Parse hook trigger.
- parameter object: The `ParseObject` the trigger should act on.
- parameter trigger: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
init<T>(object: T, trigger: ParseHookTriggerType, url: URL) where T: ParseObject {
self.init(className: T.className, trigger: trigger, url: url)
}

/**
Creates a new Parse hook trigger.
- parameter object: The `ParseObject` the trigger should act on.
- parameter triggerName: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
@available(*, deprecated, message: "Change \"triggerName\" to \"trigger\"")
init<T>(object: T, triggerName: ParseHookTriggerType, url: URL) where T: ParseObject {
self.init(className: T.className, triggerName: triggerName, url: url)
self.init(object: object, trigger: triggerName, url: url)
}

/**
Creates a new `ParseFile` or `ParseHookTriggerType.beforeConnect` hook trigger.
- parameter triggerName: The `ParseHookTriggerType` type.
- parameter trigger: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
init(triggerName: ParseHookTriggerType, url: URL) throws {
init(trigger: ParseHookTriggerType, url: URL) throws {
self.init()
self.triggerName = triggerName
self.triggerName = trigger
self.url = url
switch triggerName {
case .beforeSave, .afterSave, .beforeDelete, .afterDelete:
Expand All @@ -66,23 +99,46 @@ public extension ParseHookTriggerable {
message: "This initializer should only be used for \"ParseFile\" and \"beforeConnect\"")
}
}

/**
Creates a new `ParseFile` or `ParseHookTriggerType.beforeConnect` hook trigger.
- parameter triggerName: The `ParseHookTriggerType` type.
- parameter url: The endpoint of the hook.
*/
@available(*, deprecated, message: "Change \"triggerName\" to \"trigger\"")
init(triggerName: ParseHookTriggerType, url: URL) throws {
try self.init(trigger: triggerName, url: url)
}

}

/// A type of request for Parse Hook Triggers.
public struct TriggerRequest: Encodable {
let className: String
let triggerName: ParseHookTriggerType
let trigger: ParseHookTriggerType
let url: URL?

init<T>(trigger: T) throws where T: ParseHookTriggerable {
/**
Creates an instance.
- parameter trigger: A type that conforms to `ParseHookTriggerable`.
- throws: An error of `ParseError` type.
*/
public init<T>(trigger: T) throws where T: ParseHookTriggerable {
guard let className = trigger.className,
let triggerName = trigger.triggerName else {
let triggerType = trigger.triggerName else {
throw ParseError(code: .otherCause,
message: "The \"className\" and \"triggerName\" needs to be set: \(trigger)")
}
self.className = className
self.triggerName = triggerName
self.trigger = triggerType
self.url = trigger.url
}

enum CodingKeys: String, CodingKey {
case className
case trigger = "triggerName"
case url
}
}

// MARK: Fetch
Expand Down
19 changes: 19 additions & 0 deletions Sources/ParseSwift/Types/ParseHookFunction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ParseHookFunction.swift
// ParseSwift
//
// Created by Corey Baker on 6/23/23.
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
//

import Foundation

/**
A generic Parse Hook Function type that can be used to create any Parse Hook Function.
*/
public struct ParseHookFunction: ParseHookFunctionable {
public var functionName: String?
public var url: URL?

public init() {}
}
20 changes: 20 additions & 0 deletions Sources/ParseSwift/Types/ParseHookTrigger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// ParseHookTrigger.swift
// ParseSwift
//
// Created by Corey Baker on 6/23/23.
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
//

import Foundation

/**
A generic Parse Hook Trigger type that can be used to create any Hook Trigger.
*/
public struct ParseHookTrigger: ParseHookTriggerable {
public var className: String?
public var triggerName: ParseHookTriggerType?
public var url: URL?

public init() {}
}
Loading

0 comments on commit 66627b5

Please sign in to comment.