client parameter handler refactoring
This commit is contained in:
parent
ec610690fb
commit
606dbf6131
@ -22,7 +22,7 @@
|
|||||||
/// completionHandler can return flat dictinary or a map. It depends on the paramters handler
|
/// completionHandler can return flat dictinary or a map. It depends on the paramters handler
|
||||||
|
|
||||||
|
|
||||||
@objcMembers open class ClientParameterHandler: NSObject {
|
@objc open class ClientParameterHandler: NSObject {
|
||||||
|
|
||||||
public static let DefaultTimeout = 30.0
|
public static let DefaultTimeout = 30.0
|
||||||
|
|
||||||
@ -47,62 +47,214 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? {
|
open func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? {
|
||||||
let data = try JSONSerialization.data(withJSONObject: clientParameters)
|
let data = try JSONSerialization.data(withJSONObject: clientParameters)
|
||||||
return try JSONDecoder.create().decode(ClientParameterModel.self, from: data)
|
return try JSONDecoder.create().decode(ClientParameterModel.self, from: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
open func getParameters(with clientParameters: [String: Any], requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: Any]?) -> ()) throws {
|
open func getParameters(with clientParameters: [String: Any], requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: Any]?) -> ()) throws {
|
||||||
guard let clientParameterModel = try getClientParameterModel(clientParameters) else {
|
guard let clientParameterModel = try getClientParameterModel(clientParameters) else {
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Task {
|
getParameters(with: clientParameterModel, requestParameters: requestParameters, actionId: actionId, completionHandler: completionHandler)
|
||||||
let parameters = await getParameters(with: clientParameterModel, requestParameters: requestParameters, actionId: actionId)
|
}
|
||||||
completionHandler(parameters)
|
|
||||||
|
// open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
||||||
|
// let timeout = TimeInterval(10)//model.timeout ?? Self.DefaultTimeout)
|
||||||
|
// let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
||||||
|
// let parameters = await withThrowingTaskGroup(of: (any ClientParameterProtocol, [String: AnyHashable]).self, returning: [String: AnyHashable].self) { group in
|
||||||
|
//
|
||||||
|
// // Task for time out.
|
||||||
|
// group.addTask {
|
||||||
|
// try Task.checkCancellation()
|
||||||
|
// try await Task.sleep(nanoseconds: UInt64(timeout * TimeInterval(NSEC_PER_SEC)))
|
||||||
|
// try Task.checkCancellation()
|
||||||
|
// throw MVMCoreActionUtilityError.timeOut
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for handler in parameterHandlerList {
|
||||||
|
// group.addTask{
|
||||||
|
// try Task.checkCancellation()
|
||||||
|
// print("vvvv start \(handler.clientParameterModel.type) \(handler.clientParameterModel.id)")
|
||||||
|
// MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(
|
||||||
|
// name: handler.clientParameterModel.type,
|
||||||
|
// uuid: handler.clientParameterModel.id,
|
||||||
|
// actionId: actionId))
|
||||||
|
// let parameter = await handler.fetchClientParameters(requestParameters: requestParameters, timingOutIn: timeout)
|
||||||
|
// print("vvvv end \(handler.clientParameterModel.type) \(handler.clientParameterModel.id)")
|
||||||
|
// MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(
|
||||||
|
// name: handler.clientParameterModel.type,
|
||||||
|
// uuid: handler.clientParameterModel.id,
|
||||||
|
// actionId: actionId))
|
||||||
|
// return (handler, parameter)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// //
|
||||||
|
// // return parameter
|
||||||
|
// // } catch MVMCoreActionUtilityError.timeOut {
|
||||||
|
// // // The client parameter timed out.
|
||||||
|
// // MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterTimeout(
|
||||||
|
// // name: handler.clientParameterModel.type,
|
||||||
|
// // uuid: handler.clientParameterModel.id,
|
||||||
|
// // actionId: actionId))
|
||||||
|
// // return handler.valueOnTimeout()
|
||||||
|
// // } catch {
|
||||||
|
// // if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
||||||
|
// // MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
|
// // }
|
||||||
|
// // return [handler.clientParameterModel.type: "failed_to_collect"]
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// var finishedHandlers: [String: [String: AnyHashable]] = [:]
|
||||||
|
// //var parameters = [String: AnyHashable]()
|
||||||
|
// do {
|
||||||
|
// for try await currentParameter in group {
|
||||||
|
// print("vvvv finished \(currentParameter.0.clientParameterModel.type) \(currentParameter.0.clientParameterModel.id)")
|
||||||
|
// finishedHandlers[currentParameter.0.clientParameterModel.id] = currentParameter.1
|
||||||
|
// guard finishedHandlers.count >= parameterHandlerList.count else { continue }
|
||||||
|
// print("vvvv finished all handlers")
|
||||||
|
// // Cancel the timeout
|
||||||
|
// group.cancelAll()
|
||||||
|
// }
|
||||||
|
// } catch MVMCoreActionUtilityError.timeOut {
|
||||||
|
// // The client parameter timed out. Log each event and set failure values for unfinished handlers.
|
||||||
|
// print("vvvv timeout \(timeout)")
|
||||||
|
// group.cancelAll()
|
||||||
|
// for handler in parameterHandlerList {
|
||||||
|
// guard finishedHandlers[handler.clientParameterModel.id] == nil else { continue }
|
||||||
|
// print("vvvv timeout \(handler.clientParameterModel.type) \(handler.clientParameterModel.id)")
|
||||||
|
// MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterTimeout(
|
||||||
|
// name: handler.clientParameterModel.type,
|
||||||
|
// uuid: handler.clientParameterModel.id,
|
||||||
|
// actionId: actionId))
|
||||||
|
// finishedHandlers[handler.clientParameterModel.id] = handler.valueOnTimeout()
|
||||||
|
// }
|
||||||
|
// } catch {
|
||||||
|
// if !(error is CancellationError) {
|
||||||
|
// print("vvvv error \(error.localizedDescription)")
|
||||||
|
// // Log the error and set failure values for unfinished handlers.
|
||||||
|
// group.cancelAll()
|
||||||
|
// if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
||||||
|
// MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
|
// }
|
||||||
|
// for handler in parameterHandlerList {
|
||||||
|
// guard finishedHandlers[handler.clientParameterModel.id] == nil else { continue }
|
||||||
|
// finishedHandlers[handler.clientParameterModel.id] = handler.valueOnTimeout()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// print("vvvv finishing group")
|
||||||
|
// return finishedHandlers.reduce(into: [String: AnyHashable]()) { partialResult, parameter in
|
||||||
|
// partialResult.merge(parameter.value) { first, last in first }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// print("vvvv parameters \(parameters)")
|
||||||
|
//
|
||||||
|
// return parameters
|
||||||
|
// }
|
||||||
|
// open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
||||||
|
// }
|
||||||
|
|
||||||
|
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
||||||
|
await withCheckedContinuation { continuation in
|
||||||
|
getParameters(with: model, requestParameters: requestParameters, actionId: actionId) { parameters in
|
||||||
|
continuation.resume(returning: parameters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
|
private func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: AnyHashable]) -> ()) {
|
||||||
let timeout = TimeInterval(model.timeout ?? Self.DefaultTimeout)
|
|
||||||
|
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
|
||||||
|
let group = DispatchGroup()
|
||||||
|
let timeout = model.timeout ?? Self.DefaultTimeout
|
||||||
|
|
||||||
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
|
||||||
return await withTaskGroup(of: [String: AnyHashable].self, returning: [String: AnyHashable].self) { group in
|
let requestUUID = (0..<parameterHandlerList.count).map { _ in UUID().uuidString }
|
||||||
for handler in parameterHandlerList {
|
var returnedList = [[String: AnyHashable]?](repeating: nil, count: parameterHandlerList.count)
|
||||||
group.addTask{
|
|
||||||
// Fetch the client parameter.
|
// Dispatch setup on queue to ensure setup is complete before completion callbacks.
|
||||||
do {
|
// Don't use [weak self]. Object is deallocated in the dispatch queue.
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(
|
parametersWorkQueue.async(group: group, qos: .userInitiated) {
|
||||||
name: handler.clientParameterModel.type,
|
|
||||||
uuid: handler.clientParameterModel.id,
|
var mergedParametersList: [String: AnyHashable] {
|
||||||
actionId: actionId))
|
return returnedList.enumerated().map { (index, element) -> [String: AnyHashable] in
|
||||||
let parameter: [String: AnyHashable] = try await MVMCoreActionUtility.perform(withTimeout: timeout) {
|
guard let parameter = element else {
|
||||||
let parameter = await handler.fetchClientParameters(requestParameters: requestParameters, timingOutIn: timeout)
|
let handler = parameterHandlerList[index]
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(
|
print("vvvv timeout \(handler.clientParameterModel.type) \(handler.clientParameterModel.id)")
|
||||||
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: handler.clientParameterModel.type,
|
name: handler.clientParameterModel.type,
|
||||||
uuid: handler.clientParameterModel.id,
|
uuid: handler.clientParameterModel.id,
|
||||||
actionId: actionId))
|
actionId: actionId))
|
||||||
return handler.valueOnTimeout()
|
return handler.valueOnTimeout()
|
||||||
} catch {
|
|
||||||
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
|
|
||||||
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
|
|
||||||
}
|
|
||||||
return [handler.clientParameterModel.type: "failed_to_collect"]
|
|
||||||
}
|
}
|
||||||
|
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) {
|
||||||
|
let params = mergedParametersList
|
||||||
|
print("vvvv timeout parameters \(params)")
|
||||||
|
completionHandler(params);
|
||||||
|
complete = true
|
||||||
|
}
|
||||||
|
let completionWorkItem = DispatchWorkItem(qos: .userInitiated) {
|
||||||
|
timeoutWorkItem.cancel()
|
||||||
|
print("vvvv finishing group")
|
||||||
|
if !complete { // In the case of firing after timeout.
|
||||||
|
let params = mergedParametersList
|
||||||
|
print("vvvv finishing group \(params)")
|
||||||
|
completionHandler(params);
|
||||||
|
complete = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await group.reduce(into: [String: AnyHashable]()) { partialResult, parameter in
|
// Setup timeout.
|
||||||
partialResult.merge(parameter) { first, last in first }
|
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()
|
||||||
|
print("vvvv start \(parameterHandler.clientParameterModel.type) \(parameterHandler.clientParameterModel.id)")
|
||||||
|
parameterHandler.fetchClientParametersBridge(requestParameters: requestParameters,
|
||||||
|
timingOutIn: timeout) { (receivedParameter) in
|
||||||
|
print("vvvv end \(parameterHandler.clientParameterModel.type) \(parameterHandler.clientParameterModel.id)")
|
||||||
|
// 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.
|
||||||
|
group.notify(queue: parametersWorkQueue, work: completionWorkItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension ClientParameterProtocol {
|
||||||
|
func fetchClientParametersBridge(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completion: @escaping ([String: AnyHashable]?) -> ()) {
|
||||||
|
Task {
|
||||||
|
let parameters = await fetchClientParameters(requestParameters: requestParameters, timingOutIn: timeout)
|
||||||
|
completion(parameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user