From 29daee83340f44312ae75ea59aa9c5993ad420cc Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Mon, 21 Sep 2015 14:02:31 -0400 Subject: [PATCH 1/3] Pendo.io service for Tower UI This commit integrates the pendo support for the Tower UI, which involves creating an 'options' object and registering it with the pendo service installed in the index.html file. The options object is identified each time the user logs into Tower or when the browser session is refreshed. --- awx/ui/client/src/app.js | 6 +- .../authentication.service.js | 3 +- .../login/loginModal/loginModal.controller.js | 8 +- awx/ui/client/src/login/main.js | 2 + .../client/src/login/pendo/pendo.service.js | 137 ++++++++++++++++++ awx/ui/templates/ui/index.html | 9 ++ 6 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 awx/ui/client/src/login/pendo/pendo.service.js diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 7dbf97285f..4e598b7be8 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -74,6 +74,7 @@ __deferLoadIfEnabled(); /*#endif#*/ var tower = angular.module('Tower', [ + 'pendolytics', 'ngRoute', 'ngSanitize', 'ngCookies', @@ -830,9 +831,9 @@ var tower = angular.module('Tower', [ }]) .run(['$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'HideStream', 'Socket', - 'LoadConfig', 'Store', 'ShowSocketHelp', 'AboutAnsibleHelp', + 'LoadConfig', 'Store', 'ShowSocketHelp', 'AboutAnsibleHelp', 'pendoService', function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, HideStream, Socket, - LoadConfig, Store, ShowSocketHelp, AboutAnsibleHelp) { + LoadConfig, Store, ShowSocketHelp, AboutAnsibleHelp, pendoService) { var sock; @@ -1010,6 +1011,7 @@ var tower = angular.module('Tower', [ // when the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket) if($location.$$url !== '/login'){ $rootScope.$emit('OpenSocket'); + pendoService.issuePendoIdentity(); } } diff --git a/awx/ui/client/src/login/authenticationServices/authentication.service.js b/awx/ui/client/src/login/authenticationServices/authentication.service.js index 0473f1bd7e..ae14be47e5 100644 --- a/awx/ui/client/src/login/authenticationServices/authentication.service.js +++ b/awx/ui/client/src/login/authenticationServices/authentication.service.js @@ -1,4 +1,4 @@ - /************************************************* +/************************************************* * Copyright (c) 2015 Ansible, Inc. * * All Rights Reserved @@ -109,6 +109,7 @@ export default setLicense: function (data) { var license = data.license_info; + license.analytics_status = data.analytics_status; license.version = data.version; license.tested = false; Store('license', license); diff --git a/awx/ui/client/src/login/loginModal/loginModal.controller.js b/awx/ui/client/src/login/loginModal/loginModal.controller.js index 2bd204c072..5aeb6a2eb3 100644 --- a/awx/ui/client/src/login/loginModal/loginModal.controller.js +++ b/awx/ui/client/src/login/loginModal/loginModal.controller.js @@ -55,12 +55,11 @@ */ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', '$location', 'Authorization', 'ToggleClass', 'Alert', 'Wait', - 'Timer', 'Empty', 'ClearScope', '$scope', + 'Timer', 'Empty', 'ClearScope', '$scope', 'pendoService', function ($log, $cookieStore, $compile, $window, $rootScope, $location, Authorization, ToggleClass, Alert, Wait, - Timer, Empty, ClearScope, scope) { + Timer, Empty, ClearScope, scope, pendoService) { - var setLoginFocus, lastPath, lastUser, sessionExpired, loginAgain, - e, html; + var setLoginFocus, lastPath, lastUser, sessionExpired, loginAgain; setLoginFocus = function () { // Need to clear out any open dialog windows that might be open when this modal opens. @@ -143,6 +142,7 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', '$l Authorization.getLicense() .success(function (data) { Authorization.setLicense(data); + pendoService.issuePendoIdentity(); Wait("stop"); if (lastPath() && lastUser()) { // Go back to most recent navigation path diff --git a/awx/ui/client/src/login/main.js b/awx/ui/client/src/login/main.js index e4326a7d89..8e38a03f8b 100644 --- a/awx/ui/client/src/login/main.js +++ b/awx/ui/client/src/login/main.js @@ -9,9 +9,11 @@ import loginModal from './loginModal/main'; import loginRoute from './login.route'; import logoutRoute from './logout.route'; +import pendoService from './pendo/pendo.service'; export default angular.module('login', [authentication.name, loginModal.name]) + .service('pendoService', pendoService) .config(['$routeProvider', function($routeProvider) { var url = loginRoute.route; delete loginRoute.route; diff --git a/awx/ui/client/src/login/pendo/pendo.service.js b/awx/ui/client/src/login/pendo/pendo.service.js new file mode 100644 index 0000000000..51e2e56fe1 --- /dev/null +++ b/awx/ui/client/src/login/pendo/pendo.service.js @@ -0,0 +1,137 @@ +/************************************************* +* Copyright (c) 2015 Ansible, Inc. +* +* All Rights Reserved +*************************************************/ + + +export default + [ '$rootScope', '$pendolytics', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', + 'Store', '$log', + function ($rootScope, $pendolytics, Rest, GetBasePath, ProcessErrors, $q, + Store, $log) { + return { + setPendoOptions: function (config) { + var options = { + visitor: { + id: null, + role: null, + email: null + }, + account: { + id: null, + planLevel: config.license_type, + planPrice: config.instance_count, + creationDate: config.license_date, + trial: config.trial + } + }; + if(config.analytics_status === 'detailed'){ + this.setDetailed(options, config); + } + else if(config.analytics_status === 'anonymous'){ + this.setAnonymous(options); + } + return options; + + }, + + setDetailed: function(options, config) { + // Detailed mode + // VisitorId: username+hash of license_key + // AccountId: hash of license_key from license + // email: contact_email from license OR email from Tower account + + options.visitor.id = $rootScope.current_user.username + '@' + config.deployment_id; + options.account.id = config.deployment_id; + options.visitor.email = $rootScope.current_user.email; + }, + + setAnonymous: function (options) { + //Anonymous mode + // VisitorId: + // AccountId: + // email: + + options.visitor.id = 0; + options.account.id = "tower.ansible.com"; + options.visitor.email = ""; + }, + + setRole: function(options) { + var deferred = $q.defer(); + if($rootScope.current_user.is_superuser === true){ + options.visitor.role = 'admin'; + deferred.resolve(options); + } + else{ + var url = GetBasePath('users') + $rootScope.current_user.id + '/admin_of_organizations/'; + Rest.setUrl(url); + var promise = Rest.get(); + promise.then(function (response) { + if(response.data.count > 0 ) { + options.visitor.role = "orgadmin"; + deferred.resolve(options); + } + else { + options.visitor.role = "user"; + deferred.resolve(options); + } + }); + promise.catch(function (response) { + ProcessErrors($rootScope, response.data, response.status, null, { + hdr: 'Error!', + msg: 'Failed to get inventory name. GET returned status: ' + + response.status }); + }); + } + return deferred.promise; + }, + + getConfig: function () { + var config = Store('license'), + deferred = $q.defer(); + if(_.isEmpty(config)){ + var url = GetBasePath('config'); + Rest.setUrl(url); + var promise = Rest.get(); + promise.then(function (response) { + config = response.license_info; + config.analytics_status = response.analytics_status; + if(config.analytics_status !== 'off'){ + deferred.resolve(config); + } + else { + deferred.reject('Pendo is turned off.'); + } + }); + promise.catch(function (response) { + ProcessErrors($rootScope, response.data, response.status, null, { + hdr: 'Error!', + msg: 'Failed to get inventory name. GET returned status: ' + + response.status }); + }); + } + else if(config.analytics_status !== 'off'){ + deferred.resolve(config); + } + else { + deferred.reject('Pendo is turned off.'); + } + return deferred.promise; + }, + + issuePendoIdentity: function () { + var that = this; + this.getConfig().then(function(config){ + var options = that.setPendoOptions(config); + that.setRole(options).then(function(options){ + $pendolytics.identify(options); + }); + }, function(reason){ + $log.debug(reason); + }); + } + }; + } +]; diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 74ce0464a2..637cc36312 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -26,6 +26,15 @@ + + + From 0c7507d3a0e3073d8d281c0eb51f5a0d1aff74d4 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 30 Sep 2015 10:20:08 -0400 Subject: [PATCH 2/3] moving pendo service to login/authentication folder --- awx/ui/client/src/login/authenticationServices/main.js | 4 +++- .../login/{pendo => authenticationServices}/pendo.service.js | 0 awx/ui/client/src/login/main.js | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) rename awx/ui/client/src/login/{pendo => authenticationServices}/pendo.service.js (100%) diff --git a/awx/ui/client/src/login/authenticationServices/main.js b/awx/ui/client/src/login/authenticationServices/main.js index c6e4818cb3..a7de90ecc7 100644 --- a/awx/ui/client/src/login/authenticationServices/main.js +++ b/awx/ui/client/src/login/authenticationServices/main.js @@ -8,10 +8,12 @@ import authenticationService from './authentication.service'; import checkAccess from './checkAccess.factory'; import isAdmin from './isAdmin.factory'; import timer from './timer.factory'; +import pendoService from './pendo.service'; export default angular.module('authentication', []) .factory('Authorization', authenticationService) .factory('CheckAccess', checkAccess) .factory('IsAdmin', isAdmin) - .factory('Timer', timer); + .factory('Timer', timer) + .service('pendoService', pendoService); diff --git a/awx/ui/client/src/login/pendo/pendo.service.js b/awx/ui/client/src/login/authenticationServices/pendo.service.js similarity index 100% rename from awx/ui/client/src/login/pendo/pendo.service.js rename to awx/ui/client/src/login/authenticationServices/pendo.service.js diff --git a/awx/ui/client/src/login/main.js b/awx/ui/client/src/login/main.js index 8e38a03f8b..e4326a7d89 100644 --- a/awx/ui/client/src/login/main.js +++ b/awx/ui/client/src/login/main.js @@ -9,11 +9,9 @@ import loginModal from './loginModal/main'; import loginRoute from './login.route'; import logoutRoute from './logout.route'; -import pendoService from './pendo/pendo.service'; export default angular.module('login', [authentication.name, loginModal.name]) - .service('pendoService', pendoService) .config(['$routeProvider', function($routeProvider) { var url = loginRoute.route; delete loginRoute.route; From 879941e029e9c8434b3318e06ba93e19c472e1ea Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 30 Sep 2015 13:51:01 -0400 Subject: [PATCH 3/3] rejecting promises if erroring out --- .../src/login/authenticationServices/pendo.service.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/awx/ui/client/src/login/authenticationServices/pendo.service.js b/awx/ui/client/src/login/authenticationServices/pendo.service.js index 51e2e56fe1..e20002f6a7 100644 --- a/awx/ui/client/src/login/authenticationServices/pendo.service.js +++ b/awx/ui/client/src/login/authenticationServices/pendo.service.js @@ -83,6 +83,7 @@ export default hdr: 'Error!', msg: 'Failed to get inventory name. GET returned status: ' + response.status }); + deferred.reject('Could not resolve pendo role.'); }); } return deferred.promise; @@ -110,6 +111,7 @@ export default hdr: 'Error!', msg: 'Failed to get inventory name. GET returned status: ' + response.status }); + deferred.reject('Could not resolve pendo config.'); }); } else if(config.analytics_status !== 'off'){ @@ -127,8 +129,12 @@ export default var options = that.setPendoOptions(config); that.setRole(options).then(function(options){ $pendolytics.identify(options); + }, function(reason){ + // reject function for setRole + $log.debug(reason); }); }, function(reason){ + // reject function for getConfig $log.debug(reason); }); }