async await client parameters
This commit is contained in:
parent
e04bd2cefe
commit
f5e8c0b2e6
@ -58,78 +58,46 @@
|
|||||||
getParameters(with: clientParameterModel, requestParameters: requestParameters, actionId: actionId, completionHandler: completionHandler)
|
getParameters(with: clientParameterModel, requestParameters: requestParameters, actionId: actionId, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: Any]?) -> ()) {
|
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
||||||
|
|
||||||
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
|
|
||||||
let group = DispatchGroup()
|
|
||||||
let timeout = model.timeout ?? Self.DefaultTimeout
|
let timeout = model.timeout ?? Self.DefaultTimeout
|
||||||
|
|
||||||
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
||||||
let requestUUID = (0..<parameterHandlerList.count).map { _ in UUID().uuidString }
|
return await withTaskGroup(of: [String: AnyHashable]?.self, returning: [String: AnyHashable].self) { group in
|
||||||
var returnedList = [[String: AnyHashable]?](repeating: nil, count: parameterHandlerList.count)
|
for handler in parameterHandlerList {
|
||||||
|
group.addTask{
|
||||||
// Dispatch setup on queue to ensure setup is complete before completion callbacks.
|
// Fetch the client parameter.
|
||||||
// Don't use [weak self]. Object is deallocated in the dispatch queue.
|
do {
|
||||||
parametersWorkQueue.async(group: group, qos: .userInitiated) {
|
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(
|
||||||
|
name: handler.clientParameterModel.type,
|
||||||
var mergedParametersList: [String: AnyHashable] {
|
uuid: handler.clientParameterModel.id,
|
||||||
return returnedList.enumerated().map { (index, element) -> [String: AnyHashable] in
|
actionId: actionId))
|
||||||
guard let parameter = element else {
|
let parameter: [String: AnyHashable]? = try await MVMCoreActionUtility.perform(withTimeout: .now() + .seconds(Int(timeout))) {
|
||||||
|
let parameter = await handler.fetchClientParameters(requestParameters: requestParameters, timingOutIn: timeout)
|
||||||
|
|
||||||
|
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(
|
||||||
|
name: handler.clientParameterModel.type,
|
||||||
|
uuid: handler.clientParameterModel.id,
|
||||||
|
actionId: actionId))
|
||||||
|
return parameter
|
||||||
|
}
|
||||||
|
return parameter
|
||||||
|
} catch MVMCoreActionUtilityError.timeOut {
|
||||||
|
// The client parameter timed out.
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterTimeout(
|
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterTimeout(
|
||||||
name: model.list[index].type,
|
name: model.list[index].type,
|
||||||
uuid: requestUUID[index],
|
uuid: handler.clientParameterModel.id,
|
||||||
actionId: actionId))
|
actionId: actionId))
|
||||||
return parameterHandlerList[index].valueOnTimeout()
|
return handler.valueOnTimeout()
|
||||||
}
|
} catch {
|
||||||
return parameter
|
return nil
|
||||||
}.reduce(into: [String: AnyHashable]()) { partialResult, next in
|
|
||||||
partialResult.merge(next) { first, last in first }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup completion handlers. Barriered to ensure one happens after the other.
|
|
||||||
var complete = false
|
|
||||||
let timeoutWorkItem = DispatchWorkItem(qos: .userInitiated) {
|
|
||||||
completionHandler(mergedParametersList);
|
|
||||||
complete = true
|
|
||||||
}
|
|
||||||
let completionWorkItem = DispatchWorkItem(qos: .userInitiated) {
|
|
||||||
timeoutWorkItem.cancel()
|
|
||||||
if !complete { // In the case of firing after timeout.
|
|
||||||
completionHandler(mergedParametersList);
|
|
||||||
complete = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup timeout.
|
|
||||||
parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem)
|
|
||||||
|
|
||||||
// Setup the parameter execution.
|
|
||||||
for (index, parameterHandler) in parameterHandlerList.enumerated() {
|
|
||||||
let parameterType = parameterHandler.clientParameterModel.type
|
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(name: parameterType, uuid: requestUUID[index], actionId: actionId))
|
|
||||||
group.enter()
|
|
||||||
parameterHandler.fetchClientParameters(requestParameters: requestParameters,
|
|
||||||
timingOutIn: timeout) { (receivedParameter) in
|
|
||||||
// Queue the results for merge.
|
|
||||||
parametersWorkQueue.async {
|
|
||||||
guard !complete else {
|
|
||||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Client \(parameterType) responded after task completion.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard returnedList[index] == nil else {
|
|
||||||
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId))
|
|
||||||
returnedList[index] = receivedParameter
|
|
||||||
group.leave() // Leaving is only done after setup (barriered).
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback when all parameters have been merged.
|
return await group.reduce(into: [String: AnyHashable]()) { partialResult, parameter in
|
||||||
group.notify(queue: parametersWorkQueue, work: completionWorkItem);
|
if let parameter = parameter {
|
||||||
|
partialResult.merge(parameter) { first, last in first }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,13 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// A protocol defining models for client parameter fetching handlers.
|
||||||
public protocol ClientParameterModelProtocol: ModelProtocol {
|
public protocol ClientParameterModelProtocol: ModelProtocol {
|
||||||
|
/// The type of client parameter.
|
||||||
var type: String { get }
|
var type: String { get }
|
||||||
|
|
||||||
|
/// A unique identifier for this instance.
|
||||||
|
var id: String { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension ClientParameterModelProtocol {
|
public extension ClientParameterModelProtocol {
|
||||||
|
|||||||
@ -20,23 +20,22 @@ public protocol AnyClientParameterProtocol {
|
|||||||
init(clientParameterModel: ClientParameterModelProtocol) throws
|
init(clientParameterModel: ClientParameterModelProtocol) throws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A protocol defining client parameter fetching handlers.
|
||||||
public protocol ClientParameterProtocol<Model>: ModelHandlerProtocol, AnyClientParameterProtocol {
|
public protocol ClientParameterProtocol<Model>: ModelHandlerProtocol, AnyClientParameterProtocol {
|
||||||
associatedtype Model: ClientParameterModelProtocol
|
|
||||||
|
|
||||||
static var name: String { get }
|
associatedtype Model: ClientParameterModelProtocol
|
||||||
|
|
||||||
var clientParameterModel: Model { get set }
|
var clientParameterModel: Model { get set }
|
||||||
|
init(clientParameterModel: Model)
|
||||||
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completionHandler:@escaping ([String: AnyHashable]?) -> ())
|
|
||||||
|
|
||||||
/// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values.
|
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval) async -> [String: AnyHashable]?
|
||||||
func valueOnTimeout() -> [String: AnyHashable]
|
|
||||||
|
|
||||||
init(clientParameterModel: Model)
|
/// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values.
|
||||||
|
func valueOnTimeout() -> [String: AnyHashable]
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension ClientParameterProtocol {
|
public extension ClientParameterProtocol {
|
||||||
/// Default Implementation should never be overridden
|
/// Helper init to convert protocol to specific model. Default Implementation should never be overridden
|
||||||
init(clientParameterModel: ClientParameterModelProtocol) throws {
|
init(clientParameterModel: ClientParameterModelProtocol) throws {
|
||||||
// Cast the clientParameterModel to ensure it matches the asscociatedType Model assigned to this Handler.
|
// Cast the clientParameterModel to ensure it matches the asscociatedType Model assigned to this Handler.
|
||||||
guard let castedModel = clientParameterModel as? Model else {
|
guard let castedModel = clientParameterModel as? Model else {
|
||||||
@ -48,7 +47,7 @@ public extension ClientParameterProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func valueOnTimeout() -> [String: AnyHashable] {
|
func valueOnTimeout() -> [String: AnyHashable] {
|
||||||
return [Self.name: "failed_to_collect"]
|
return [clientParameterModel.type: "failed_to_collect"]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The handler should call this method to pass the parameter back to the caller.
|
/// The handler should call this method to pass the parameter back to the caller.
|
||||||
@ -57,7 +56,7 @@ public extension ClientParameterProtocol {
|
|||||||
if isFlatMap {
|
if isFlatMap {
|
||||||
completionHandler(parameter)
|
completionHandler(parameter)
|
||||||
} else {
|
} else {
|
||||||
completionHandler([Self.name: parameter])
|
completionHandler([clientParameterModel.type: parameter])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user