Merge branch 'feature/client_paramter_processing' into 'develop'

Feature/client paramter processing

See merge request BPHV_MIPS/mvm_core!146
This commit is contained in:
Suresh, Kamlesh Jain 2021-03-10 17:08:10 -05:00
commit 55dbe0e4dd
4 changed files with 114 additions and 66 deletions

View File

@ -32,7 +32,7 @@ extern NSString * _Nonnull const KeyActionTypeOpen;
- (void)synchronouslyHandleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - (void)synchronouslyHandleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Iterates through the clientParameters list. Gets values from the individual handlers and attaches the parameters to extraParameters. /// Iterates through the clientParameters list. Gets values from the individual handlers and attaches the parameters to extraParameters.
- (void)setClientParameter:(nullable NSDictionary *)actionInformation completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; - (void)getClientParameter:(nullable NSDictionary *)clientParametersMap requestParameters:(nullable NSDictionary *)requestParameters showLoadingOverlay:(BOOL)showLoadingOverlay completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler;
#pragma mark - Actions #pragma mark - Actions
/// by default, returns the original RequestParameter that passed in. Can be overriden for some generic updates to the RequestParameter before handle open page action gets called. /// by default, returns the original RequestParameter that passed in. Can be overriden for some generic updates to the RequestParameter before handle open page action gets called.
@ -97,7 +97,7 @@ extern NSString * _Nonnull const KeyActionTypeOpen;
+ (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; + (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Sends the request to the load handler. /// Sends the request to the load handler.
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; + (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// By default, throws an error, calling defaultHandleActionError. /// By default, throws an error, calling defaultHandleActionError.
+ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; + (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;

View File

@ -93,22 +93,19 @@ NSString * const KeyActionTypeOpen = @"openPage";
} }
} }
- (void)setClientParameter:(nullable NSDictionary *)actionInformation completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { - (void)getClientParameter:(nullable NSDictionary *)clientParametersMap requestParameters:(nullable NSDictionary *)requestParameters showLoadingOverlay:(BOOL)showLoadingOverlay completionHandler:(nonnull void (^)(NSDictionary * _Nullable parameters))completionHandler {
NSDictionary *clientParametersMap = [actionInformation dict:KeyClientParameters];
if (!clientParametersMap) { if (!clientParametersMap) {
completionHandler(actionInformation); completionHandler(nil);
return; return;
} }
BOOL isBackgroudRequest = [actionInformation boolForKey:@"background"]; if (showLoadingOverlay) {
if (!isBackgroudRequest) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading]; [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
} }
void (^stopLoadingOverlay)(void) = ^(void) { void (^stopLoadingOverlay)(void) = ^(void) {
if (!isBackgroudRequest) { if (showLoadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:true]; [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:true];
} }
}; };
@ -116,27 +113,23 @@ NSString * const KeyActionTypeOpen = @"openPage";
NSError *error = nil; NSError *error = nil;
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Fetching client parameters"]; [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Fetching client parameters"];
[[[MVMCoreObject sharedInstance] clientParameterRegistry] getParametersWith:clientParametersMap [[[MVMCoreObject sharedInstance] clientParameterRegistry] getParametersWith:clientParametersMap
requestParameters:requestParameters
error:&error error:&error
completionHandler:^(NSDictionary<NSString *,id> * _Nullable clientParameters) { completionHandler:^(NSDictionary<NSString *,id> * _Nullable clientParameters) {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Finshed fetching client parameters"]; [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Finshed fetching client parameters"];
if (clientParameters) { if (clientParameters) {
NSMutableDictionary *actionWithClientParameters = [actionInformation mutableCopy];
NSMutableDictionary *extraParameters = [clientParameters mutableCopy];
[extraParameters addEntriesFromDictionary:[actionWithClientParameters dictionaryForKey:KeyExtraParameters]];
actionWithClientParameters[KeyExtraParameters] = extraParameters;
stopLoadingOverlay(); stopLoadingOverlay();
completionHandler(actionWithClientParameters); completionHandler(clientParameters);
} else { } else {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"No client parameters"]; [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"No client parameters"];
stopLoadingOverlay(); stopLoadingOverlay();
completionHandler(actionInformation); completionHandler(nil);
} }
}]; }];
if (error) { if (error) {
stopLoadingOverlay(); stopLoadingOverlay();
completionHandler(actionInformation); completionHandler(nil);
[MVMCoreLoggingHandler addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:@"MVMCoreActionHandler->setClientParameter"]]; [MVMCoreLoggingHandler addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:@"MVMCoreActionHandler->setClientParameter"]];
} }
} }
@ -168,20 +161,17 @@ NSString * const KeyActionTypeOpen = @"openPage";
return; return;
} }
__weak typeof(self) weakSelf = self; MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionInformation];
void (^performAction)(NSDictionary*) = ^(NSDictionary* actionMap) { [self updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) {
MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionMap]; if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData];
[weakSelf updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) { } else {
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData]; actionInformation:actionInformation
} else { additionalData:additionalData
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegateObject:delegateObject]; delegateObject:delegateObject];
} }
}]; }];
};
[self setClientParameter:actionInformation completionHandler:performAction];
} }
- (void)shareAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - (void)shareAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
@ -278,7 +268,10 @@ NSString * const KeyActionTypeOpen = @"openPage";
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage]; [delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage];
} else { } else {
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegateObject:delegateObject]; [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters
actionInformation:actionInformation
additionalData:additionalData
delegateObject:delegateObject];
} }
}]; }];
}]; }];
@ -340,26 +333,35 @@ NSString * const KeyActionTypeOpen = @"openPage";
- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
void (^performAction)(NSDictionary*) = ^(NSDictionary* actionMap) { void (^performAction)(NSDictionary*) = ^(NSDictionary* extraParamters) {
NSMutableDictionary *actionWithClientParameters = [actionInformation mutableCopy];
NSMutableDictionary *extraParametersT = [extraParamters mutableCopy];
[extraParametersT addEntriesFromDictionary:[actionWithClientParameters dictionaryForKey:KeyExtraParameters]];
actionWithClientParameters[KeyExtraParameters] = extraParametersT;
// Gets the app url // Gets the app url
NSURL *appURL = nil; NSURL *appURL = nil;
NSString *appURLString = [actionMap string:KeyLinkAwayAppURL]; NSString *appURLString = [actionWithClientParameters string:KeyLinkAwayAppURL];
if (appURLString.length > 0) { if (appURLString.length > 0) {
appURL = [NSURL URLWithString:appURLString]; appURL = [NSURL URLWithString:appURLString];
} }
// Gets the browser url // Gets the browser url
NSURL *otherURL = nil; NSURL *otherURL = nil;
NSString *otherURLString = [actionMap string:KeyLinkAwayURL]; NSString *otherURLString = [actionWithClientParameters string:KeyLinkAwayURL];
if (otherURLString.length > 0) { if (otherURLString.length > 0) {
otherURL = [NSURL URLWithString:otherURLString]; otherURL = [NSURL URLWithString:otherURLString];
} }
// Provide the URL and App URL to be modified if needed by a subclass or delegate. // Provide the URL and App URL to be modified if needed by a subclass or delegate.
[weakSelf prepareLinkAwayWithURL:otherURL appURL:appURL actionInformation:actionMap additionalData:additionalData delegateObject:delegateObject]; [weakSelf prepareLinkAwayWithURL:otherURL appURL:appURL actionInformation:actionWithClientParameters additionalData:additionalData delegateObject:delegateObject];
}; };
[self setClientParameter:actionInformation completionHandler:performAction]; [self getClientParameter:[actionInformation dict:KeyClientParameters]
requestParameters:nil
showLoadingOverlay:true
completionHandler:performAction];
} }
- (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
@ -418,8 +420,20 @@ NSString * const KeyActionTypeOpen = @"openPage";
// Currently no default log action but this will eventually be server driven. // Currently no default log action but this will eventually be server driven.
} }
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { + (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
NSDictionary *clientParamters = [actionInformation dict:KeyClientParameters];
if (clientParamters) {
[[MVMCoreActionHandler sharedActionHandler] getClientParameter:clientParamters
requestParameters: requestParameters.parameters
showLoadingOverlay: !requestParameters.backgroundRequest
completionHandler: ^(NSDictionary * _Nullable jsonDictionary) {
[requestParameters addRequestParameters:jsonDictionary];
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
}];
} else {
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
}
} }
+ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { + (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {

View File

@ -9,7 +9,33 @@
import Foundation import Foundation
public protocol ClientParameterProtocol { public protocol ClientParameterProtocol {
init()
static var name: String { get } static var name: String { get }
func fetchClientParameters(for paramModel: ClientParameterModelProtocol, timingOutIn timeout: Double, completionHandler:@escaping (AnyHashable?) -> ())
init(_ clientParameterModel: ClientParameterModelProtocol)
var clientParameterModel: ClientParameterModelProtocol { get set }
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: Double, completionHandler:@escaping ([String: AnyHashable]?) -> ())
/// 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 {
func valueOnTimeout() -> [String: AnyHashable] {
return [Self.name: "failed_to_collect"]
}
/// The handler should call this method to pass the parameter back to the caller.
func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable]?, completionHandler: @escaping ([String: AnyHashable]?) -> ()) {
guard let parameter = parameter else {
return completionHandler(nil)
}
if isFlatMap {
completionHandler(parameter)
} else {
completionHandler([Self.name: parameter])
}
}
} }

View File

@ -23,9 +23,9 @@ import Foundation
mapping[T.name] = handler mapping[T.name] = handler
} }
open func createParametersHandler(_ actionType: String) -> ClientParameterProtocol? { open func createParametersHandler(_ clientParameterModel: ClientParameterModelProtocol) -> ClientParameterProtocol? {
guard let parameterType = mapping[actionType] else { return nil } guard let parameterType = mapping[clientParameterModel.type] else { return nil }
return parameterType.init() return parameterType.init(clientParameterModel)
} }
static func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? { static func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? {
@ -47,33 +47,52 @@ import Foundation
/// ] /// ]
///} ///}
/// 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
open func getParameters(with clientParameters: [String: Any], completionHandler:@escaping ([String: Any]?) -> ()) throws { open func getParameters(with clientParameters: [String: Any], requestParameters: [String: Any], completionHandler:@escaping ([String: Any]?) -> ()) throws {
guard let clientParameterModel = try ClientParameterRegistry.getClientParameterModel(clientParameters) else { guard let clientParameterModel = try ClientParameterRegistry.getClientParameterModel(clientParameters) else {
completionHandler(nil) completionHandler(nil)
return return
} }
var parametersList: [String: Any] = [:]
let timeout = clientParameterModel.timeout ?? 30.0 let timeout = clientParameterModel.timeout ?? 30.0
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter") let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
let group = DispatchGroup() let group = DispatchGroup()
let defaultErrorString = "failed_to_collect."
// Dispatch setup on queue to ensure setup is complete before completion callbacks. // Dispatch setup on queue to ensure setup is complete before completion callbacks.
parametersWorkQueue.async(group: group, qos: .userInitiated) { [weak self] in parametersWorkQueue.async(group: group, qos: .userInitiated) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
var parameterHandlerList: [ClientParameterProtocol] = []
// Create the handler list so that same object can be used when merging. Merging needs default value in case of timeout
for parameterModel in clientParameterModel.list {
if let parameterHandler = self.createParametersHandler(parameterModel) {
parameterHandlerList.append(parameterHandler)
}
}
var returnedList = [[String: AnyHashable]?](repeating: nil, count: parameterHandlerList.count)
var mergedParametersList: [String: AnyHashable] {
var parametersList: [String: AnyHashable] = [:]
for (index, item) in returnedList.enumerated() {
let parameter = item ?? parameterHandlerList[index].valueOnTimeout()
parametersList = parametersList.merging(parameter) { (_, new) in new }
}
return parametersList
}
// Setup completion handlers. Barriered to ensure one happens after the other. // Setup completion handlers. Barriered to ensure one happens after the other.
var complete = false var complete = false
let timeoutWorkItem = DispatchWorkItem(qos: .userInitiated) { let timeoutWorkItem = DispatchWorkItem(qos: .userInitiated) {
completionHandler(parametersList); completionHandler(mergedParametersList);
complete = true complete = true
} }
let completionWorkItem = DispatchWorkItem(qos: .userInitiated) { let completionWorkItem = DispatchWorkItem(qos: .userInitiated) {
timeoutWorkItem.cancel() timeoutWorkItem.cancel()
if !complete { // In the case of firing after timeout. if !complete { // In the case of firing after timeout.
completionHandler(parametersList); completionHandler(mergedParametersList);
complete = true complete = true
} }
} }
@ -82,17 +101,13 @@ import Foundation
parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem) parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem)
// Setup the parameter execution. // Setup the parameter execution.
for parameterModel in clientParameterModel.list { for (index, parameterHandler) in parameterHandlerList.enumerated() {
// Setup default timeout / nil error. This will be replaced as parameters are collected.
parametersList[parameterModel.type] = ["error": defaultErrorString]
group.enter() group.enter()
// Dispatch asynchronous injection. parameterHandler.fetchClientParameters(requestParameters: requestParameters,
self.getParameterFromHandler(parameterModel, before: timeout) { (receivedParameter) in timingOutIn: timeout) { (receivedParameter) in
// Queue the results for merge. // Queue the results for merge.
parametersWorkQueue.async { parametersWorkQueue.async {
if let receivedParameter = receivedParameter { returnedList[index] = receivedParameter
parametersList[parameterModel.type] = receivedParameter
}
group.leave() // Leaving is only done after setup (barriered). group.leave() // Leaving is only done after setup (barriered).
} }
} }
@ -103,13 +118,6 @@ import Foundation
} }
} }
func getParameterFromHandler( _ parameterModel: ClientParameterModelProtocol, before timeout: Double, completionHandler:@escaping (AnyHashable?) -> ()) {
guard let parameterHandler = createParametersHandler(parameterModel.type) else {
return completionHandler(nil)
}
parameterHandler.fetchClientParameters(for: parameterModel, timingOutIn: timeout, completionHandler: completionHandler)
}
/// Add all registry here. /// Add all registry here.
open func registerParameters() { } open func registerParameters() { }