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)
|
||||
}
|
||||
|
||||
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: Any]?) -> ()) {
|
||||
|
||||
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
|
||||
let group = DispatchGroup()
|
||||
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
||||
let timeout = model.timeout ?? Self.DefaultTimeout
|
||||
|
||||
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
||||
let requestUUID = (0..<parameterHandlerList.count).map { _ in UUID().uuidString }
|
||||
var returnedList = [[String: AnyHashable]?](repeating: nil, count: parameterHandlerList.count)
|
||||
|
||||
// Dispatch setup on queue to ensure setup is complete before completion callbacks.
|
||||
// Don't use [weak self]. Object is deallocated in the dispatch queue.
|
||||
parametersWorkQueue.async(group: group, qos: .userInitiated) {
|
||||
|
||||
var mergedParametersList: [String: AnyHashable] {
|
||||
return returnedList.enumerated().map { (index, element) -> [String: AnyHashable] in
|
||||
guard let parameter = element else {
|
||||
return await withTaskGroup(of: [String: AnyHashable]?.self, returning: [String: AnyHashable].self) { group in
|
||||
for handler in parameterHandlerList {
|
||||
group.addTask{
|
||||
// Fetch the client parameter.
|
||||
do {
|
||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(
|
||||
name: handler.clientParameterModel.type,
|
||||
uuid: handler.clientParameterModel.id,
|
||||
actionId: actionId))
|
||||
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(
|
||||
name: model.list[index].type,
|
||||
uuid: requestUUID[index],
|
||||
uuid: handler.clientParameterModel.id,
|
||||
actionId: actionId))
|
||||
return parameterHandlerList[index].valueOnTimeout()
|
||||
}
|
||||
return parameter
|
||||
}.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).
|
||||
return handler.valueOnTimeout()
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback when all parameters have been merged.
|
||||
group.notify(queue: parametersWorkQueue, work: completionWorkItem);
|
||||
|
||||
return await group.reduce(into: [String: AnyHashable]()) { partialResult, parameter in
|
||||
if let parameter = parameter {
|
||||
partialResult.merge(parameter) { first, last in first }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,13 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A protocol defining models for client parameter fetching handlers.
|
||||
public protocol ClientParameterModelProtocol: ModelProtocol {
|
||||
/// The type of client parameter.
|
||||
var type: String { get }
|
||||
|
||||
/// A unique identifier for this instance.
|
||||
var id: String { get }
|
||||
}
|
||||
|
||||
public extension ClientParameterModelProtocol {
|
||||
|
||||
@ -20,23 +20,22 @@ public protocol AnyClientParameterProtocol {
|
||||
init(clientParameterModel: ClientParameterModelProtocol) throws
|
||||
}
|
||||
|
||||
/// A protocol defining client parameter fetching handlers.
|
||||
public protocol ClientParameterProtocol<Model>: ModelHandlerProtocol, AnyClientParameterProtocol {
|
||||
associatedtype Model: ClientParameterModelProtocol
|
||||
|
||||
static var name: String { get }
|
||||
|
||||
var clientParameterModel: Model { get set }
|
||||
|
||||
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completionHandler:@escaping ([String: AnyHashable]?) -> ())
|
||||
associatedtype Model: ClientParameterModelProtocol
|
||||
|
||||
var clientParameterModel: Model { get set }
|
||||
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]
|
||||
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval) async -> [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 {
|
||||
/// Default Implementation should never be overridden
|
||||
/// Helper init to convert protocol to specific model. Default Implementation should never be overridden
|
||||
init(clientParameterModel: ClientParameterModelProtocol) throws {
|
||||
// Cast the clientParameterModel to ensure it matches the asscociatedType Model assigned to this Handler.
|
||||
guard let castedModel = clientParameterModel as? Model else {
|
||||
@ -48,7 +47,7 @@ public extension ClientParameterProtocol {
|
||||
}
|
||||
|
||||
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.
|
||||
@ -57,7 +56,7 @@ public extension ClientParameterProtocol {
|
||||
if isFlatMap {
|
||||
completionHandler(parameter)
|
||||
} else {
|
||||
completionHandler([Self.name: parameter])
|
||||
completionHandler([clientParameterModel.type: parameter])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user