From 6214b5cfb75f426e84e6928e71fd93df609f8bec Mon Sep 17 00:00:00 2001 From: Joris Tirado Date: Tue, 23 Jun 2020 09:57:06 -0700 Subject: [PATCH] MSPB-123: Resolve misconfigured feature codes generically (#222) * Restructure feature code recovery logic * Refactor feature code recovery logic Generically recover feature code with incorrect entries (patterns/numbers). We delete malformed feature codes to mitigate the use case of feature codes wrongly configured with entries matching another feature code, making it impossible to patch (duplicate entries error). * Refactor createCallflow to handle errors --- submodules/strategy/strategy.js | 201 ++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 86 deletions(-) diff --git a/submodules/strategy/strategy.js b/submodules/strategy/strategy.js index b1fe49b..c77c2e8 100644 --- a/submodules/strategy/strategy.js +++ b/submodules/strategy/strategy.js @@ -41,13 +41,13 @@ define(function(require) { 'MainHolidays' ], - featureCodes: [ + featureCodeConfigs: [ { name: 'directed_ext_pickup', number: '87', pattern: '^\\*87([0-9]+)$', moduleName: 'group_pickup_feature', - extraData: { + metadata: { type: 'extension' } }, @@ -113,7 +113,7 @@ define(function(require) { pattern: '^\\*98([0-9]*)$', moduleName: 'voicemail', actionName: 'check', - extraData: { + metadata: { single_mailbox_login: true } }, @@ -3463,92 +3463,95 @@ define(function(require) { strategyHandleFeatureCodes: function() { var self = this, - featureCodesToUpdate = [ - 'voicemail[action=check]', - 'voicemail[single_mailbox_login]' - ], - expectedFeaturedCodes = _.keyBy(self.featureCodes, 'name'); + featureCodeConfigs = _.keyBy(self.featureCodeConfigs, 'name'), + configToEntries = { + pattern: 'patterns', + callflowNumber: 'numbers' + }, + isMalformed = function isMalformed(featureCode) { + var config = _.get(featureCodeConfigs, featureCode.featurecode.name), + configProp = _ + .chain(configToEntries) + .keys() + .find(_.partial(_.has, config)) + .value(), + expectedEntry = _.get(config, configProp), + entriesProp = _.get(configToEntries, configProp), + entries = _.get(featureCode, entriesProp); + + return _.isUndefined(config) + || !_.isEqual(entries, [expectedEntry]); + }, + createFeatureCodeFactory = function createFeatureCodeFactory(featureCode) { + return function(callback) { + self.strategyCreateCallflow({ + bypassProgressIndicator: true, + data: { + data: _.merge({ + flow: _.merge({ + children: {}, + data: _.get(featureCode, 'metadata', {}), + module: featureCode.moduleName + }, _.has(featureCode, 'actionName') && { + data: { + action: featureCode.actionName + } + }), + featurecode: _.pick(featureCode, [ + 'name', + 'number' + ]) + }, _.has(featureCode, 'pattern') ? { + patterns: [featureCode.pattern] + } : { + numbers: [featureCode.callflowNumber] + }) + }, + success: _.partial(callback, null), + error: _.partial(callback, null) + }); + }; + }; monster.waterfall([ - function(callback) { - self.strategyGetFeatureCodes(function(featureCodes) { - callback(null, featureCodes); - }); + function fetchExistingFeatureCodes(callback) { + self.strategyGetFeatureCodes(_.partial(callback, null)); }, - function(featureCodes, callback) { - var featureCodeNames = _.map(featureCodes, 'featurecode.name'); - + function maybeDeleteMalformedFeatureCodes(existing, callback) { monster.parallel(_ - .chain(self.featureCodes) - .reject(function(featureCode) { - return _.includes(featureCodeNames, featureCode.name); - }) - .map(function(featureCode) { - var newCallflow = { - flow: { - children: {}, - data: _.get(featureCode, 'extraData', {}), - module: featureCode.moduleName - }, - featurecode: { - name: featureCode.name, - number: featureCode.number - } - }; - - if (_.has(featureCode, 'actionName')) { - _.set(newCallflow, 'flow.data.action', featureCode.actionName); - } - if (_.has(featureCode, 'pattern')) { - _.set(newCallflow, 'patterns', [featureCode.pattern]); - } else { - _.set(newCallflow, 'numbers', [featureCode.callflowNumber]); - } - - return function(parallelCallback) { - self.strategyCreateCallflow({ + .chain(existing) + .filter(isMalformed) + .keyBy('id') + .mapValues(function(featureCode) { + return function(callback) { + self.strategyDeleteCallflow({ + bypassProgressIndicator: true, data: { - data: newCallflow + callflowId: featureCode.id }, - success: function() { - parallelCallback(null); - } + success: _.partial(callback, null), + error: _.partial(callback, null) }); }; }) .value() - , function() { - callback(null, featureCodes); + , function(err, results) { + callback(null, _.reject(existing, _.flow([ + _.partial(_.get, _, 'id'), + _.partial(_.includes, _.keys(results)) + ]))); }); }, - function(featureCodes, callback) { - monster.parallel( - _.chain(featureCodes) - .filter(function(featureCode) { - return !_.includes( - _.get(featureCode, 'patterns', []), - _.get(expectedFeaturedCodes, [featureCode.featurecode.name, 'pattern']) - ) && _.includes(featureCodesToUpdate, featureCode.featurecode.name); - }) - .map(function(featureCode) { - return function(parallelCallback) { - self.strategyPatchCallflow({ - data: { - callflowId: featureCode.id, - data: { - patterns: [_.get(expectedFeaturedCodes, [featureCode.featurecode.name, 'pattern'])], - numbers: [] - } - }, - callback: function() { - parallelCallback(null); - } - }); - }; - }) - .value(), - callback - ); + function maybeCreateMissingFeatureCodes(existing, callback) { + monster.parallel(_ + .chain(self.featureCodeConfigs) + .reject(_.flow([ + _.partial(_.get, _, 'name'), + _.partial(_.includes, _.map(existing, 'featurecode.name')) + ])) + .map(createFeatureCodeFactory) + .value() + , callback); } ]); }, @@ -3557,6 +3560,7 @@ define(function(require) { var self = this; self.strategyListCallflows({ + bypassProgressIndicator: true, filters: { paginate: 'false', has_key: 'featurecode' @@ -3985,7 +3989,7 @@ define(function(require) { strategyListCallflows: function(args) { var self = this; - self.callApi({ + self.callApi(_.merge({ resource: 'callflow.list', data: { accountId: self.accountId, @@ -3997,22 +4001,28 @@ define(function(require) { error: function(parsedError) { args.hasOwnProperty('error') && args.error(parsedError); } - }); + }, _.has(args, 'bypassProgressIndicator') && { + bypassProgressIndicator: true + })); }, strategyCreateCallflow: function(args) { var self = this; - self.callApi({ + self.callApi(_.merge({ resource: 'callflow.create', - data: { - accountId: self.accountId, - data: args.data.data + data: _.merge({ + accountId: self.accountId + }, args.data), + success: function(data) { + _.has(args, 'success') && args.success(data.data); }, - success: function(callflowData) { - args.hasOwnProperty('success') && args.success(callflowData.data); + error: function(parsedError) { + _.has(args, 'error') && args.error(parsedError); } - }); + }, _.has(args, 'bypassProgressIndicator') && { + bypassProgressIndicator: true + })); }, strategyUpdateCallflow: function(callflow, callback) { @@ -4047,6 +4057,25 @@ define(function(require) { }); }, + strategyDeleteCallflow: function(args) { + var self = this; + + self.callApi(_.merge({ + resource: 'callflow.delete', + data: _.merge({ + accountId: self.accountId + }, args.data), + success: function(data) { + _.has(args, 'success') && args.success(data.data); + }, + error: function(parsedError) { + _.has(args, 'error') && args.error(parsedError); + } + }, _.has(args, 'bypassProgressIndicator') && { + bypassProgressIndicator: true + })); + }, + strategyListDirectories: function(callbackSuccess, callbackError) { var self = this;