From 9526975318697f57dcf5fb5a7628f0523efd3de9 Mon Sep 17 00:00:00 2001 From: Joe Fiorini Date: Wed, 3 Jun 2015 00:12:00 -0400 Subject: [PATCH] Resolve fact versions before looking up fact views This adds a step to the fact version lookup that attempts to resolve fact versions before looking up their views. Resolving a fact version means finding another version to use when there is a problem with the one the user selected. In this case, the resolution process works as follows: 1. Check if either request returned an empty list If so, request again, changing the dates to look back a year. This will almost always return something. 2. If we are requesting multiple facts for the same host, and both of the resolved versions have the same timestamp, they are duplicates and we cannot compare against them. Instead we instruct it to grab the selected and the previous versions for comparison. For host-to-host view, this also updates the timestamp in the header to reflect the actual timestamps. It _does not_ update the date picker dates yet. --- .../data-services/fact-scan-data.service.js | 56 ++++++++------ .../get-data-for-comparison.factory.js | 60 ++++++++------- .../data-services/resolve-versions.factory.js | 75 +++++++++++++++++++ awx/ui/static/js/system-tracking/main.js | 2 + .../system-tracking.controller.js | 21 ++++-- .../system-tracking.partial.html | 4 +- .../system-tracking/system-tracking.route.js | 24 +++--- 7 files changed, 169 insertions(+), 73 deletions(-) create mode 100644 awx/ui/static/js/system-tracking/data-services/resolve-versions.factory.js diff --git a/awx/ui/static/js/system-tracking/data-services/fact-scan-data.service.js b/awx/ui/static/js/system-tracking/data-services/fact-scan-data.service.js index 7187a4078d..eb77caecad 100644 --- a/awx/ui/static/js/system-tracking/data-services/fact-scan-data.service.js +++ b/awx/ui/static/js/system-tracking/data-services/fact-scan-data.service.js @@ -6,13 +6,36 @@ export default ['Rest', 'GetBasePath', 'ProcessErrors', 'lodashAsPromised', function (Rest, GetBasePath, ProcessErrors, _) { + + function buildUrl (host_id, module, startDate, endDate) { + var url = GetBasePath('hosts') + host_id + '/fact_versions/', + params= [["module", module] , ['from', startDate.format()], ['to', endDate.format()]]; + + params = params.filter(function(p){ + return !_.isEmpty(p[1]); + }); + params = params.map(function(p){ + return p.join("="); + }).join("&"); + url = _.compact([url, params]).join("?"); + return url; + } + return { getHostFacts: function(host, moduleName, date, fetchScanNumber) { - var version =this.getVersion(host, moduleName, date.from, date.to, fetchScanNumber); + var getVersion = _.partial(this.getVersion, host, moduleName); var getFacts = this.getFacts; - return version + return getVersion(date.from, date.to, fetchScanNumber) + .then(function(versionData) { + if (_.isEmpty(versionData)) { + var retryStartDate = date.from.clone().subtract(1, 'year'); + return getVersion(retryStartDate, date.from, fetchScanNumber); + } else { + return versionData; + } + }) .then(function(versionData) { if (_.isEmpty(versionData)) { return { fact: [] }; @@ -37,18 +60,21 @@ function (Rest, GetBasePath, ProcessErrors, _) { }); }, - getVersion: function(host_id, module, startDate, endDate, fetchScanNumber){ + getVersion: function(versionParams){ //move the build url into getVersion and have the // parameters passed into this - var promise, - url = this.buildUrl(host_id, module, startDate, endDate); + var promise; + var hostId = versionParams.hostId; + var startDate = versionParams.dateRange.from; + var endDate = versionParams.dateRange.to; + var module = versionParams.moduleName; - fetchScanNumber = fetchScanNumber || 0; + var url = buildUrl(hostId, module, startDate, endDate); Rest.setUrl(url); promise = Rest.get(); - return promise.then(function(data) { - return data.data.results[fetchScanNumber]; + return promise.then(function(response) { + return response.data.results; }).catch(function (response) { ProcessErrors(null, response.data, response.status, null, { hdr: 'Error!', @@ -56,20 +82,6 @@ function (Rest, GetBasePath, ProcessErrors, _) { response.status }); }); - }, - - buildUrl: function(host_id, module, startDate, endDate){ - var url = GetBasePath('hosts') + host_id + '/fact_versions/', - params= [["module", module] , ['from', startDate.format()], ['to', endDate.format()]]; - - params = params.filter(function(p){ - return !_.isEmpty(p[1]); - }); - params = params.map(function(p){ - return p.join("="); - }).join("&"); - url = _.compact([url, params]).join("?"); - return url; } }; }]; diff --git a/awx/ui/static/js/system-tracking/data-services/get-data-for-comparison.factory.js b/awx/ui/static/js/system-tracking/data-services/get-data-for-comparison.factory.js index 42c8796181..8179085d22 100644 --- a/awx/ui/static/js/system-tracking/data-services/get-data-for-comparison.factory.js +++ b/awx/ui/static/js/system-tracking/data-services/get-data-for-comparison.factory.js @@ -7,8 +7,9 @@ export default [ 'factScanDataService', 'getModuleOptions', + 'resolveVersions', 'lodashAsPromised', - function(factScanDataService, getModuleOptions, _) { + function(factScanDataService, getModuleOptions, resolveVersions) { return function(hostIds, moduleName, leftDate, rightDate) { var moduleOptions; @@ -17,39 +18,40 @@ export default hostIds = hostIds.concat(hostIds[0]); } + var hostVersionParams = + [{ hostId: hostIds[0], + dateRange: leftDate, + moduleName: moduleName + }, + { hostId: hostIds[1], + dateRange: rightDate, + moduleName: moduleName + } + ]; + return getModuleOptions(hostIds[0]) .then(function(modules) { moduleOptions = modules; - return modules; - }).then(function() { - return hostIds; - }).thenMap(function(hostId, index) { - var date = leftDate; - var fetchScanNumber; - - if (index === 1) { - date = rightDate; - } else { - if (rightDate.from.isSame(leftDate.from, 'day')) { - fetchScanNumber = 1; - } - } - - var params = - [ hostId, - moduleName, - date, - fetchScanNumber + return hostVersionParams; + }).thenMap(function(versionParam) { + var versionWithRequest = + [ versionParam, + factScanDataService. + getVersion(versionParam) ]; - return params; - }).thenMap(function(params) { - var getHostFacts = - _.spread(factScanDataService.getHostFacts) - .bind(factScanDataService); - - return getHostFacts(params); - }).then(function(hostFacts) { + return versionWithRequest; + }).thenAll(function(versions) { + return resolveVersions(versions); + }, true) + .thenMap(function(versionData) { + if (versionData) { + return factScanDataService.getFacts(versionData); + } else { + return { fact: [] }; + } + }) + .thenAll(function(hostFacts) { return [moduleOptions, hostFacts]; }); }; diff --git a/awx/ui/static/js/system-tracking/data-services/resolve-versions.factory.js b/awx/ui/static/js/system-tracking/data-services/resolve-versions.factory.js new file mode 100644 index 0000000000..bf6898c34a --- /dev/null +++ b/awx/ui/static/js/system-tracking/data-services/resolve-versions.factory.js @@ -0,0 +1,75 @@ +function resolveVersions(service, _, results) { + + function transformToObjects(versionArray) { + + var converted = versionArray[0]; + converted.versions = versionArray[1]; + + return converted; + + } + + function resolveEmpties(result) { + if (_.isEmpty(result.versions)) { + var originalStartDate = result.dateRange.from; + result.dateRange.from = originalStartDate.clone().subtract(1, 'year'); + result.dateRange.to = originalStartDate; + return [result, service.getVersion(result)]; + } + + return [result, _.promise(result.versions)]; + } + + function resolveDuplicates(nonEmptyResults) { + var allSameHost = + _.every(nonEmptyResults, { 'hostId': nonEmptyResults[0].hostId }); + + if (allSameHost) { + var firstTimestamp = nonEmptyResults[0].versions[0].timestamp; + + var hostIdsWithDupes = + _(nonEmptyResults) + .pluck('versions[0]') + .filter('timestamp', firstTimestamp) + .map(function(version, index) { + return nonEmptyResults[index].hostId; + }) + .value(); + + if (hostIdsWithDupes.length === 1) { + return _.pluck(nonEmptyResults, 'versions[0]'); + } + + return nonEmptyResults.map(function(scan, index) { + var hasDupe = + _.include(hostIdsWithDupes, scan.hostId); + + if (hasDupe && index === 1) { + return scan.versions[1]; + } else { + return scan.versions[0]; + } + }); + + } else { + return _.pluck(nonEmptyResults, 'versions[0]'); + } + } + + return _(results) + .map(transformToObjects) + .map(resolveEmpties) + .thenAll(function(resolved) { + var versionObjects = resolved.map(transformToObjects); + return resolveDuplicates(versionObjects); + }, true) + .value(); +} + +export default + [ 'factScanDataService', + 'lodashAsPromised', + function(factScanDataService, lodash) { + return _.partial(resolveVersions, factScanDataService, lodash); + } + ]; diff --git a/awx/ui/static/js/system-tracking/main.js b/awx/ui/static/js/system-tracking/main.js index 3a42fd821e..c1e94bd617 100644 --- a/awx/ui/static/js/system-tracking/main.js +++ b/awx/ui/static/js/system-tracking/main.js @@ -8,6 +8,7 @@ import route from './system-tracking.route'; import factScanDataService from './data-services/fact-scan-data.service'; import getDataForComparison from './data-services/get-data-for-comparison.factory'; import getModuleOptions from './data-services/get-module-options.factory'; +import resolveVersions from './data-services/resolve-versions.factory'; import controller from './system-tracking.controller'; import stringOrDateFilter from './string-or-date.filter'; import shared from 'tower/shared/main'; @@ -23,6 +24,7 @@ export default .service('factScanDataService', factScanDataService) .factory('getDataForComparison', getDataForComparison) .factory('getModuleOptions', getModuleOptions) + .factory('resolveVersions', resolveVersions) .filter('stringOrDate', stringOrDateFilter) .controller('systemTracking', controller) .config(['$routeProvider', function($routeProvider) { diff --git a/awx/ui/static/js/system-tracking/system-tracking.controller.js b/awx/ui/static/js/system-tracking/system-tracking.controller.js index f6fca4ccde..223df0cade 100644 --- a/awx/ui/static/js/system-tracking/system-tracking.controller.js +++ b/awx/ui/static/js/system-tracking/system-tracking.controller.js @@ -24,8 +24,8 @@ function controller($rootScope, $scope.hostIds = $routeParams.hosts; $scope.inventory = $routeParams.model.inventory; - $scope.factModulePickersLabelLeft = "Compare facts collected on"; - $scope.factModulePickersLabelRight = "To facts collected on"; + $scope.factModulePickersLabelLeft = "Compare facts collected on or before"; + $scope.factModulePickersLabelRight = "To facts collected on or before"; $scope.modules = initialFactData.moduleOptions; @@ -40,6 +40,9 @@ function controller($rootScope, $scope.leftDate = initialFactData.leftSearchRange.from; $scope.rightDate = initialFactData.rightSearchRange.from; + $scope.leftScanDate = initialFactData.leftScanDate; + $scope.rightScanDate = initialFactData.rightScanDate; + function setHeaderValues(viewType) { if (viewType === 'singleHost') { $scope.comparisonLeftHeader = $scope.leftScanDate; @@ -58,7 +61,6 @@ function controller($rootScope, var leftRange = searchConfig.leftRange; var rightRange = searchConfig.rightRange; var activeModule = searchConfig.module; - var leftScanDate, rightScanDate; if (!factData) { @@ -68,12 +70,12 @@ function controller($rootScope, activeModule.name, leftRange, rightRange) - .thenAll(function(factDataAndModules) { + .then(function(factDataAndModules) { var responses = factDataAndModules[1]; var data = _.pluck(responses, 'fact'); - leftScanDate = moment(responses[0].timestamp); - rightScanDate = moment(responses[1].timestamp); + $scope.leftScanDate = moment(responses[0].timestamp); + $scope.rightScanDate = moment(responses[1].timestamp); return data; }, true); @@ -82,7 +84,8 @@ function controller($rootScope, waitIndicator('start'); return _(factData) - .thenAll(function(facts) { + .promise() + .then(function(facts) { // Make sure we always start comparison against // a non-empty array // @@ -132,7 +135,9 @@ function controller($rootScope, $scope.factData = info; - setHeaderValues(viewType, leftScanDate, rightScanDate); + setHeaderValues(viewType); + + return info; }).finally(function() { waitIndicator('stop'); diff --git a/awx/ui/static/js/system-tracking/system-tracking.partial.html b/awx/ui/static/js/system-tracking/system-tracking.partial.html index 64c770c3b5..c928f227dc 100644 --- a/awx/ui/static/js/system-tracking/system-tracking.partial.html +++ b/awx/ui/static/js/system-tracking/system-tracking.partial.html @@ -31,10 +31,10 @@

- There were no facts collected on the dates you selected ({{error.dateValues.leftDate|amDateFormat:'L'}} and {{error.dateValues.rightDate|amDateFormat:'L'}}). Please pick a different range or module and try again. + There were no facts collected on or before the dates you selected ({{error.dateValues.leftDate|amDateFormat:'L'}} and {{error.dateValues.rightDate|amDateFormat:'L'}}). Please pick a different range or module and try again.

- There were no facts collected on one of the dates you selected ({{error.dateValue|amDateFormat:'L'}}). Please select a different date and try again. + There were no facts collected on or before one of the dates you selected ({{error.dateValue|amDateFormat:'L'}}). Please select a different date and try again.

To setup or run scan jobs, edit the "{{inventory.name}}" inventory and select "Scan Jobs Templates". diff --git a/awx/ui/static/js/system-tracking/system-tracking.route.js b/awx/ui/static/js/system-tracking/system-tracking.route.js index 3b7ee26f11..edd8a3e9e0 100644 --- a/awx/ui/static/js/system-tracking/system-tracking.route.js +++ b/awx/ui/static/js/system-tracking/system-tracking.route.js @@ -32,22 +32,22 @@ export default { var data = getDataForComparison(hostIds, moduleParam, leftDate, rightDate). - thenAll(function(factDataAndModules) { - var moduleOptions = factDataAndModules[0]; - var factResponses = factDataAndModules[1]; - var factData = _.pluck(factResponses, 'fact'); + then(function(factDataAndModules) { + var moduleOptions = factDataAndModules[0]; + var factResponses = factDataAndModules[1]; + var factData = _.pluck(factResponses, 'fact'); - factData.leftSearchRange = leftDate; - factData.rightSearchRange = rightDate; + factData.leftSearchRange = leftDate; + factData.rightSearchRange = rightDate; - factData.leftScanDate = moment(factResponses[0].timestamp); - factData.rightScanDate = moment(factResponses[0].timestamp); + factData.leftScanDate = moment(factResponses[0].timestamp); + factData.rightScanDate = moment(factResponses[1].timestamp); - factData.moduleName = moduleParam; - factData.moduleOptions = moduleOptions; + factData.moduleName = moduleParam; + factData.moduleOptions = moduleOptions; - return factData; - }, true) + return factData; + }, true) .value(); return data;