Bulk upload changes and music health check apis
[portal.git] / ecomp-portal-FE-common / client / app / views / role / bulk-upload-dialogs / bulk-upload-role-functions-controller.js
1 /*-
2  * ============LICENSE_START==========================================
3  * ONAP Portal
4  * ===================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ===================================================================
7  *
8  * Unless otherwise specified, all software contained herein is licensed
9  * under the Apache License, Version 2.0 (the "License");
10  * you may not use this software except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *             http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * Unless otherwise specified, all documentation contained herein is licensed
22  * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
23  * you may not use this documentation except in compliance with the License.
24  * You may obtain a copy of the License at
25  *
26  *             https://creativecommons.org/licenses/by/4.0/
27  *
28  * Unless required by applicable law or agreed to in writing, documentation
29  * distributed under the License is distributed on an "AS IS" BASIS,
30  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  * See the License for the specific language governing permissions and
32  * limitations under the License.
33  *
34  * ============LICENSE_END============================================
35  *
36  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
37  */
38 /**
39  * bulk upload role-functions controller
40  */
41 'use strict';
42 (function () {
43     class BulkRoleAndFunctionsModalCtrl {
44         constructor($scope, $log, $filter, $q, $modalInstance, $modal, ngDialog, message, confirmBoxService, usersService, applicationsService, functionalMenuService, RoleService) {
45                 // Set to true for copious console output
46                 var debug = false;
47                 // Roles fetched from Role service
48                 var appRoleFuncsResult = [];
49                 // Functions fetched from Role service
50                 var appFunctionsResult = [];
51                 // Global  roles fetched from Role service
52                 var appGlobalRolesResult = [];
53                 
54                 var appId = message.appid;
55                 
56                  $scope.ngRepeatBulkUploadOptions = [
57                         {id: '1', title: 'Functions', value: 'functions'},
58                         {id: '2', title: 'Roles', value: 'roles'},
59                         {id: '3', title: 'Role Functions', value: 'roleFunctions'},
60                         {id: '4', title: 'Global Role Functions', value: 'globalRoleFunctions'}
61                     ];
62                  
63                  $scope.selectedUploadType =   $scope.ngRepeatBulkUploadOptions[0];
64                  $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
65                  $scope.changeUploadTypeInstruction = function(typeInstrc){
66                          switch(typeInstrc) {
67                             case 'functions':
68                                 $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
69                                 break;
70                             case 'roles':
71                                 $scope.UploadTypeInstruction = "Role Name, Priority (Optional)";
72                                 break;
73                             case 'roleFunctions':
74                                 $scope.UploadTypeInstruction = "Role Name, Function Type, Function Instance, Function Action, Function Name";
75                                 break;
76                             default:
77                                 $scope.UploadTypeInstruction = "Global Role Name, Function Type, Function Instance, Function Action, Function Name";
78                         }
79                  };
80                 
81                 let init = () => {
82                         if (debug)
83                                 $log.debug('BulkRoleAndFunctionsModalCtrl::init');
84                         // Angular insists on this.
85                         $scope.fileModel = {};
86                         // Enable modal controls
87                         this.step1 = true;
88                         
89                         this.fileSelected = false;                      
90                 }; // init
91                 
92                 // Answers a function that compares properties with the specified name.
93                 let getSortOrder = (prop, foldCase) => {
94                 return function(a, b) {
95                         let aProp = foldCase ? a[prop].toLowerCase() : a[prop];
96                         let bProp = foldCase ? b[prop].toLowerCase() : b[prop];
97                     if (aProp > bProp)
98                         return 1;
99                     else if (aProp < bProp) 
100                         return -1;
101                     else
102                         return 0;
103                 }
104             }
105                 
106                 // Caches the file name supplied by the event handler.
107                 $scope.fileChangeHandler = (event, files) => {
108                         this.fileSelected = true;
109                         this.fileToRead = files[0];
110                         if (debug)
111                                 $log.debug("BulkRoleAndFunctionsModalCtrl::fileChangeHandler: file is ", this.fileToRead);
112                 }; // file change handler
113                 
114                 /**
115                  * Reads the contents of the file, calls portal endpoints to
116                  * validate roles, userIds and existing role assignments;
117                  * ultimately builds array of requests to be sent. Creates scope
118                  * variable with input file contents for communication with
119                  * functions.
120                  * 
121                  * This function performs a synchronous step-by-step process
122                  * using asynchronous promises. The code could all be inline
123                  * here but the nesting becomes unwieldy.
124                  */
125                 $scope.readValidateFile = (typeUpload) => {
126                         $scope.isProcessing = true;
127                         $scope.progressMsg = 'Reading upload file...';
128                         var reader = new FileReader();
129                         reader.onload = function(event) {
130                                 if(typeUpload === 'roles'){
131                                         $scope.uploadFile = $filter('csvToRoleObj')(reader.result);
132                                         if (debug){
133                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
134                                         }
135                                         $scope.progressMsg = 'Fetching & validating application roles...';
136                                                                 // fetch app roles
137                                                         RoleService.getRoles(appId).then(function (appRoles){
138                                                                 if (debug){
139                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
140                                                                 }
141                                                                         let availableRolesList = JSON.parse(appRoles.data);
142                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
143                                                                 $scope.evalAppRolesCheckResults();
144                                                                 // Re sort by line for the confirmation dialog
145                                                         $scope.uploadFile.sort(getSortOrder('line', false));
146                                                         // We're done, confirm box may show the  table
147                                                         if (debug)
148                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
149                                                         $scope.progressMsg = 'Done.';
150                                                         $scope.isProcessing = false;
151                                                         }, function(error) {
152                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
153                                                 $scope.isProcessing = false;
154                                         });
155                                 } else if (typeUpload === 'roleFunctions'){
156                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
157                                         if (debug){
158                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
159                                         }
160                                         $scope.progressMsg = 'Fetching & validating application role functions...';
161                                         //fetch app functions
162                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
163                                                 if (debug)
164                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
165                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
166                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
167                                                                 // fetch app roles
168                                                         RoleService.getRoles(appId).then(function (appRoles){
169                                                                 if (debug){
170                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
171                                                                 }
172                                                                         let availableRolesList = JSON.parse(appRoles.data);
173                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
174                                                                 $scope.evalAppRoleFuncsCheckResults();
175                                                                 // Re sort by line for the confirmation dialog
176                                                         $scope.uploadFile.sort(getSortOrder('line', false));
177                                                         // We're done, confirm box may show the  table
178                                                         if (debug)
179                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
180                                                         $scope.progressMsg = 'Done.';
181                                                         $scope.isProcessing = false;
182                                                         }, function(error) {
183                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
184                                                 $scope.isProcessing = false;
185                                         });
186                                         },
187                         function(error) {
188                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
189                                 $scope.isProcessing = false;
190                         }
191                         );
192                                 } else if(typeUpload === 'functions'){
193                                         $scope.uploadFile = $filter('csvToFuncObj')(reader.result);
194                                         if (debug){
195                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
196                                         }
197                                         $scope.progressMsg = 'Fetching & validating the application functions...';
198                                         // fetch app functions
199                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
200                                                 if (debug)
201                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
202                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
203                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
204                                                 $scope.verifyFunctions();
205                                                 $scope.evalAppFunctionsCheckResults();
206                                         // Re sort by line for the confirmation dialog
207                                         $scope.uploadFile.sort(getSortOrder('line', false));
208                                         // We're done, confirm box may show the  table
209                                         if (debug)
210                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
211                                         $scope.progressMsg = 'Done.';
212                                         $scope.isProcessing = false;
213                                         },
214                         function(error) {
215                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
216                                 $scope.isProcessing = false;
217                         }
218                         );
219                                 } else if(typeUpload === 'globalRoleFunctions'){
220                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
221                                         if (debug){
222                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
223                                         }
224                                         $scope.progressMsg = 'Fetching application global role functions...';
225                                         //fetch app functions
226                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
227                                                         if (debug)
228                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
229                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
230                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
231                                                                 // fetch app roles
232                                                         RoleService.getRoles(appId).then(function (appRoles){
233                                                                 if (debug){
234                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
235                                                                 }
236                                                                         let availableRolesList = JSON.parse(appRoles.data);
237                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
238                                                                         appRoleFuncsResult.forEach(function(appRole) {
239                                                                                 if(appRole.name.toLowerCase().startsWith("global_")){
240                                                                                         appGlobalRolesResult.push(appRole);
241                                                                                 }
242                                                                                 });
243                                                                 $scope.evalAppRoleFuncsCheckResults(typeUpload);
244                                                                 // Re sort by line for the confirmation dialog
245                                                         $scope.uploadFile.sort(getSortOrder('line', false));
246                                                         // We're done, confirm box may show the  table
247                                                         if (debug)
248                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
249                                                         $scope.progressMsg = 'Done.';
250                                                         $scope.isProcessing = false;
251                                                         }, function(error) {
252                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
253                                                 $scope.isProcessing = false;
254                                         });
255                                                 },
256                             function(error) {
257                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
258                                 $scope.isProcessing = false;
259                             }
260                             );
261                                 }
262
263                         } // onload
264                         
265                         // Invoke the reader on the selected file
266                         reader.readAsText(this.fileToRead);
267                 }; 
268                 
269                 /**
270                  * Evaluates the result set returned by the role service.
271                  * Sets an uploadFile array element status if a functions is not
272                  * defined. Reads and writes scope variable uploadFile. Reads
273                  * closure variable appFunctionsResult.
274                  */
275                 $scope.verifyFunctions = () => {
276                         if (debug)
277                                 $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: appFunctions is ' + JSON.stringify(appFunctionsResult));
278                         // check functions in upload file against defined app functions
279                         $scope.uploadFile.forEach( function (uploadRow) {
280                                 // skip rows that already have a defined status: headers etc.
281                                 if (uploadRow.status) {
282                                         if (debug)
283                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: skip row ' + uploadRow.line);
284                                         return;
285                                 }
286                                 for (var i=0; i < appFunctionsResult.length; i++) {
287                                         if (uploadRow.type.toUpperCase() === appFunctionsResult[i].type.toUpperCase()
288                                                 && uploadRow.instance.toUpperCase() === appFunctionsResult[i].code.toUpperCase()
289                                                 && uploadRow.action.toUpperCase() === appFunctionsResult[i].action.toUpperCase()) {
290                                                 if (debug)
291                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: match on function ' + uploadRow.type,
292                                                                 uploadRow.instance, uploadRow.type,  uploadRow.type);
293                                                 break;
294                                         }
295                                 }
296                         }); // foreach
297                 }; // verifyFunctions
298                 
299         /**
300                  * Evaluates the result set of existing functions returned by 
301                  * the Roleservice and list of functions found in the upload file. 
302                  * Reads and writes scope variable uploadFile. 
303                  * Reads closure variable appFunctionsResult.
304                  */
305                 $scope.evalAppFunctionsCheckResults = () => {
306                         if (debug)
307                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
308                         $scope.uploadFile.forEach(function (uploadRow) {
309                                 if (uploadRow.status) {
310                                         if (debug)
311                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: skip row ' + uploadRow.line);
312                                         return;
313                                 }
314                                 // Search for the match in the app-functions
315                                 // array
316                                 let isFunctionExist = false;
317                                 appFunctionsResult.forEach( function (exixtingFuncObj) {
318                                                         if (uploadRow.type.toUpperCase() === exixtingFuncObj.type.toUpperCase()
319                                                                 && uploadRow.instance.toUpperCase() === exixtingFuncObj.code.toUpperCase()
320                                                                 && uploadRow.action.toUpperCase() === exixtingFuncObj.action.toUpperCase()) {
321                                                                 uploadRow.status = 'Function exits!';
322                                                                 uploadRow.isCreate = false;
323                                                                 isFunctionExist = true;
324                                                         }
325                                 }); // for each result
326                                 if(!isFunctionExist) {
327                                         if(/[^a-zA-Z0-9\-\.\_]/.test(uploadRow.type) 
328                                                         || (uploadRow.action !== '*' 
329                                                         && /[^a-zA-Z0-9\-\.\_]/.test(uploadRow.action))
330                                                         || /[^a-zA-Z0-9\-\:\_\./*]/.test(uploadRow.instance)
331                                                         || /[^a-zA-Z0-9\-\_ \.]/.test(uploadRow.name)){
332                                                 uploadRow.status = 'Invalid function';
333                                                 uploadRow.isCreate = false;
334                                         } else {
335                                                 if (debug){
336                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: new function ' 
337                                                                         + uploadRow);
338                                                 }
339                                                         // After much back-and-forth I decided a clear  indicator is better than blank in the table  status column.
340                                                 uploadRow.status = 'Create';
341                                                 uploadRow.isCreate = true;
342                                         }
343                                         }
344                         }); // for each row
345                 }; // evalAppFunctionsCheckResults
346                 
347                 /**
348                  * Evaluates the result set of existing roles returned by 
349                  * the Roleservice and list of roles found in the upload file. 
350                  * Reads and writes scope variable uploadFile. 
351                  * Reads closure variable appRolesResult.
352                  */
353                         $scope.evalAppRolesCheckResults = () => {
354                                 if (debug)
355                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: uploadFile length is ' + $scope.uploadFile.length);
356                                 $scope.uploadFile.forEach(function (uploadRow) {
357                                         if (uploadRow.status) {
358                                                 if (debug)
359                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: skip row ' + uploadRow.line);
360                                                 return;
361                                         }
362                                         // Search for the match in the app-roles
363                                 // array
364                                         let isRoleExist = false;
365                                         appRoleFuncsResult.forEach( function (existingRoleObj) {
366                                                                 if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
367                                                                 uploadRow.status = 'Role exits!';
368                                                                 uploadRow.isCreate = false;
369                                                                 isRoleExist = true;
370                                                                 }
371                                         }); // for each result
372                                         if(!isRoleExist) {
373                                                 if(/[^a-zA-Z0-9\-\_ \.\/]/.test(uploadRow.role) ||
374                                                                 uploadRow.role.toLowerCase().startsWith("global_")){
375                                                         uploadRow.status = 'Invalid role!';
376                                                         uploadRow.isCreate = false;
377                                                 } else {
378                                                         if (debug){
379                                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: new function ' 
380                                                                         + uploadRow);
381                                                         }
382                                                         // After much back-and-forth I decided a clear  indicator is better than blank in the table  status column.
383                                                         uploadRow.status = 'Create';
384                                                         uploadRow.isCreate = true;
385                                                 }
386                                         }
387                                 }); // for each row
388                         }; // evalAppRolesCheckResults
389                         
390                         /**
391                          * Evaluates the result set of existing roles returned by 
392                          * the Roleservice and list of roles found in the upload file. 
393                          * Reads and writes scope variable uploadFile. 
394                          * Reads closure variable appRolesResult.
395                          */
396                         $scope.evalAppRoleFuncsCheckResults = (typeUpload) => {
397                                 if (debug)
398                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
399                                 $scope.uploadFile.forEach(function (uploadRow) {
400                                         if (uploadRow.status) {
401                                                 if (debug)
402                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: skip row ' + uploadRow.line);
403                                                 return;
404                                         }
405                                         // Search for the match in the app-functions array
406                                         let isValidFunc = false;
407                                         appFunctionsResult.forEach(function (existingFuncObj){
408                                                 if(uploadRow.type.toUpperCase() === existingFuncObj.type.toUpperCase()
409                                                                         && uploadRow.instance.toUpperCase() === existingFuncObj.code.toUpperCase()
410                                                                         && uploadRow.action.toUpperCase() === existingFuncObj.action.toUpperCase()
411                                                                         && uploadRow.name.toUpperCase() === existingFuncObj.name.toUpperCase()){
412                                                         isValidFunc = true;
413                                                         }
414                                         });
415                                         
416                                         let isValidRole = false;
417                                         let isRoleFuncExist = false;
418                                         if(typeUpload === 'globalRoleFunctions'){
419                                                 // Search for the match in the app-role array
420                                                 appGlobalRolesResult.forEach( function (existingRoleObj) {
421                                                                         if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
422                                                                                 isValidRole = true;
423                                                                                 if(isValidFunc){
424                                                                                         existingRoleObj.roleFunctions.forEach(function (existingRoleFuncObj){
425                                                                                                 if(uploadRow.type.toUpperCase() === existingRoleFuncObj.type.toUpperCase()
426                                                                                                 && uploadRow.instance.toUpperCase() === existingRoleFuncObj.code.toUpperCase()
427                                                                                                 && uploadRow.action.toUpperCase() === existingRoleFuncObj.action.toUpperCase()){
428                                                                                                         isRoleFuncExist = true;
429                                                                                         }
430                                                                                         });
431                                                                                 }
432                                                                         }
433                                                 }); // for each result
434                                         } else {
435                                                 // Search for the match in the app-role array
436                                                 appRoleFuncsResult.forEach( function (existingRoleObj) {
437                                                                         if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
438                                                                                 isValidRole = true;
439                                                                                 if(isValidFunc){
440                                                                                         existingRoleObj.roleFunctions.forEach(function (existingRoleFuncObj){
441                                                                                                 if(uploadRow.type.toUpperCase() === existingRoleFuncObj.type.toUpperCase()
442                                                                                                 && uploadRow.instance.toUpperCase() === existingRoleFuncObj.code.toUpperCase()
443                                                                                                 && uploadRow.action.toUpperCase() === existingRoleFuncObj.action.toUpperCase()){
444                                                                                                         isRoleFuncExist = true;
445                                                                                         }
446                                                                                         });
447                                                                                 }       
448                                                                         }
449                                                 }); // for each result
450                                         }
451                                         
452                                 uploadRow.isCreate = false;
453                                 if(typeUpload === 'globalRoleFunctions' && (!isValidRole || !isValidFunc)){
454                                         uploadRow.status = 'Invalid global role function!';
455                                 } else if(typeUpload !== 'globalRoleFunctions' && (!isValidRole || !isValidFunc)){
456                                         uploadRow.status = 'Invalid role function!';
457                                 } else if(typeUpload === 'globalRoleFunctions' && !isRoleFuncExist) {
458                                         uploadRow.status = 'Add global role function!';
459                                                 uploadRow.isCreate = true;
460                                 } else if(typeUpload !== 'globalRoleFunctions' && !isRoleFuncExist){
461                                         uploadRow.status = 'Add role function!';
462                                         uploadRow.isCreate = true;
463                                 } else if(typeUpload === 'globalRoleFunctions'){
464                                         uploadRow.status = 'Global role function exists!';
465                                 } else {
466                                         uploadRow.status = 'Role function exists!';
467                                 }
468                                         
469                                 }); // for each row
470                         }; // evalAppRolesCheckResults
471                         
472                 
473         /**
474          * Sends requests to Portal BE requesting application functions assignment.
475                  * That endpoint handles creation of the application functions in the 
476                  * external auth system if necessary. Reads closure variable appFunctionsResult.
477                  * Invoked by the Next button on the confirmation dialog.
478                  */
479                 $scope.updateFunctionsInDB = () => {
480                         $scope.isProcessing = true;
481                         $scope.progressMsg = 'Sending requests to application..';
482                         if (debug)
483                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: request length is ' + appUserRolesRequest.length);
484                         var numberFunctionsSucceeded = 0;
485                         let promises = [];
486                         $scope.uploadFile.forEach(function(appFuncPostData) {
487                                 if (debug) 
488                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: appFuncPostData is ' + JSON.stringify(appFuncPostData));
489                                 let updateFunctionsFinalPostData = {
490                                          type: appFuncPostData.type, 
491                                          code: appFuncPostData.instance, 
492                                          action: appFuncPostData.action,
493                                          name: appFuncPostData.name
494                         };
495                      if (debug)
496                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: updateFunctionsFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
497                      let updatePromise = {};
498                      if(appFuncPostData.isCreate){
499                      updatePromise = functionalMenuService.saveBulkFunction(appId, updateFunctionsFinalPostData).promise().then(res => {
500                          if (debug)
501                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: updated successfully: ' + JSON.stringify(res));
502                          numberFunctionsSucceeded++;
503                      }).catch(err => {
504                          // What to do if one of many fails??
505                          $log.error('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB failed: ', err);
506                          confirmBoxService.showInformation(
507                                          'Failed to update the application functions. ' +
508                                          'Error: ' + err.status).then(isConfirmed => { });
509                      }).finally( () => {
510                      });
511                         }
512                     promises.push(updatePromise);
513                  }); // for each
514                         
515                  // Run all the promises
516                  $q.all(promises).then(function(){
517                          $scope.isProcessing = false;
518                          confirmBoxService.showInformation('Processed ' + numberFunctionsSucceeded + ' records.').then(isConfirmed => {
519                                  // Close the upload-confirm dialog
520                                  ngDialog.close();
521                          });
522                  });
523              }; // updateFunctionsInDB
524              
525              /**
526                  * Sends requests to Portal BE requesting application functions assignment.
527                  * That endpoint handles creation of the application role in the 
528                  * external auth system if necessary. Reads closure variable appRoleFuncResult.
529                  * Invoked by the Next button on the confirmation dialog.
530                  */
531                         $scope.updateRolesInDB = () => {
532                                 $scope.isProcessing = true;
533                                 $scope.progressMsg = 'Sending requests to application..';
534                                 if (debug)
535                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: request length is ' + appUserRolesRequest.length);
536                                 var numberRolesSucceeded = 0;
537                                 let promises = [];
538                                 $scope.uploadFile.forEach(function(appRolePostData) {
539                                         let priority = parseInt(appRolePostData.priority);
540                                         if (debug) 
541                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: appRolePostData is ' + JSON.stringify(appFuncPostData));
542                                         let uplaodRolePostData = "";
543                                         if(isNaN(priority)){
544                                         uplaodRolePostData = {
545                                                  name: appRolePostData.role,
546                                                  active: true,
547                                                         }
548                                         } else {
549                                                 uplaodRolePostData = {
550                                          name: appRolePostData.role, 
551                                          priority: appRolePostData.priority, 
552                                          active: true,
553                                                 }
554                                         }
555                                                 var postData = {
556                                                                 role: uplaodRolePostData,
557                                                                 roleFunctions: [],
558                                                                 childRoles: []
559                                                         }
560                           if (debug)
561                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: uplaodRoleFinalPostData is ' + JSON.stringify(uplaodRoleFinalPostData));
562                           let updatePromise = {};
563                           if(appRolePostData.isCreate){
564                           updatePromise = functionalMenuService.saveBulkRole(appId, JSON.stringify(postData)).promise().then(res => {
565                                  if (debug)
566                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: updated successfully: ' + JSON.stringify(res));
567                                 numberRolesSucceeded++;
568                           }).catch(err => {
569                                  // What to do if one of many fails??
570                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateRolesInDB failed: ', err);
571                                  confirmBoxService.showInformation(
572                                                  'Failed to update the application role. ' +
573                                                  'Error: ' + err.status).then(isConfirmed => { });
574                           }).finally( () => {
575                           });
576                         }
577                          promises.push(updatePromise);
578                          }); // for each
579                                 
580                          // Run all the promises
581                          $q.all(promises).then(function(){
582                                  $scope.isProcessing = false;
583                                  confirmBoxService.showInformation('Processed ' + numberRolesSucceeded + ' records. Please sync roles').then(isConfirmed => {
584                                          // Close the upload-confirm dialog
585                                          ngDialog.close();
586                                  });
587                          });
588                   }; // updateRolesInDB
589                   
590                 /**
591                  * Sends requests to Portal BE requesting role function assignment.
592                  * That endpoint handles adding role function in the external auth system
593                  * if necessary.Invoked by the Next button on the confirmation dialog.
594                  */
595                         $scope.updateRoleFunctionsInDB = () => {
596                                 $scope.isProcessing = true;
597                                 $scope.progressMsg = 'Sending requests to application..';
598                                 if (debug)
599                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
600                                 var numberRoleFunctionSucceeded = 0;
601                                 let promises = [];
602                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
603                                         if (debug) 
604                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: appRoleFuncPostData is ' + JSON.stringify(appFuncPostData));
605                                         let updateRoleFunctionFinalPostData = {
606                                                  roleName: appRoleFuncPostData.role,
607                                                  type: appRoleFuncPostData.type, 
608                                                  instance: appRoleFuncPostData.instance, 
609                                                  action: appRoleFuncPostData.action,
610                                                  name: appRoleFuncPostData.name,
611                                                  isGlobalRolePartnerFunc: false
612                             };
613                          if (debug)
614                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: updateRoleFunctionFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
615                          let updatePromise = {};
616                          if(appRoleFuncPostData.isCreate){
617                          updatePromise = functionalMenuService.updateBulkRoleFunction(appId, updateRoleFunctionFinalPostData).promise().then(res => {
618                                  if (debug)
619                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
620                                  numberRoleFunctionSucceeded++;
621                          }).catch(err => {
622                                  // What to do if one of many fails??
623                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB failed: ', err);
624                                  confirmBoxService.showInformation(
625                                                  'Failed to update the application role function. ' +
626                                                  'Error: ' + err.status).then(isConfirmed => { });
627                          }).finally( () => {
628                          });
629                         }
630                         promises.push(updatePromise);
631                          }); // for each
632                                 
633                          // Run all the promises
634                          $q.all(promises).then(function(){
635                                  $scope.isProcessing = false;
636                                  confirmBoxService.showInformation('Processed ' + numberRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal').then(isConfirmed => {
637                                          // Close the upload-confirm dialog
638                                          ngDialog.close();
639                                  });
640                          });
641                   }; // updateRoleFunctionsInDB
642                   
643                 /**
644                          * Sends requests to Portal requesting global role functions assignment.
645                          * That endpoint handles updating global role functions in the external auth system
646                          * if necessary. Invoked by the Next button on the confirmation dialog.
647                          */
648                         $scope.updateGlobalRoleFunctionsInDB = () => {
649                                 $scope.isProcessing = true;
650                                 $scope.progressMsg = 'Sending requests to application..';
651                                 if (debug)
652                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
653                                 var numberGlobalRoleFunctionSucceeded = 0;
654                                 let promises = [];
655                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
656                                         if (debug) 
657                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: appRoleFuncPostData is ' + JSON.stringify(appFuncPostData));
658                                         let updateGlobalRoleFunctionFinalPostData = {
659                                                  roleName: appRoleFuncPostData.role,
660                                                  type: appRoleFuncPostData.type, 
661                                                  instance: appRoleFuncPostData.instance, 
662                                                  action: appRoleFuncPostData.action,
663                                                  name: appRoleFuncPostData.name,
664                                                  isGlobalRolePartnerFunc: true
665                                 };
666                              if (debug)
667                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: updateRoleFunctionFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
668                              let updatePromise = {};
669                              if(appRoleFuncPostData.isCreate){
670                              updatePromise = functionalMenuService.updateBulkRoleFunction(appId, updateGlobalRoleFunctionFinalPostData).promise().then(res => {
671                                  if (debug)
672                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
673                                  numberGlobalRoleFunctionSucceeded++;
674                              }).catch(err => {
675                                  // What to do if one of many fails??
676                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB failed: ', err);
677                                  confirmBoxService.showInformation(
678                                                  'Failed to update the global role partner function. ' +
679                                                  'Error: ' + err.status).then(isConfirmed => { });
680                              }).finally( () => {
681                              });
682                                 }
683                             promises.push(updatePromise);
684                          }); // for each
685                                 
686                          // Run all the promises
687                          $q.all(promises).then(function(){
688                                  $scope.isProcessing = false;
689                                  confirmBoxService.showInformation('Processed ' + numberGlobalRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal').then(isConfirmed => {
690                                          // Close the upload-confirm dialog
691                                          ngDialog.close();
692                                  });
693                          });
694                        }; // updateGlobalRoleFunctionsInDB
695                        
696                 // Sets the variable that hides/reveals the user controls
697                 $scope.step2 = () => {
698                         this.fileSelected = false;
699                         $scope.selectedFile = null;
700                         $scope.fileModel = null;
701                         this.step1 = false;                     
702                 }
703                 
704              // Navigate between dialog screens using step number: 1,2,...
705              $scope.navigateBack = () => {
706                  this.step1 = true;
707                  this.fileSelected = false;
708              };
709              
710              // Opens a dialog to show the data to be uploaded.
711              // Invoked by the upload button on the bulk user dialog.
712              $scope.confirmUpload = (typeUpload) => {
713                 // Start the process
714                 $scope.readValidateFile(typeUpload);
715                 // Dialog shows progress
716                 if(typeUpload === 'functions'){
717                         $modal.open({
718                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-functions-confirm.html',
719                         controller: '',
720                         sizeClass: 'modal-medium', 
721                         resolve:'',
722                         scope: $scope
723                     })
724                 } else if(typeUpload === 'roleFunctions'){
725                         $modal.open({
726                                 templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-role-functions-confirm.html',
727                         controller: '',
728                         sizeClass: 'modal-medium', 
729                         resolve:'',
730                         scope: $scope
731                     })
732                         
733                 } else if(typeUpload === 'roles'){
734                         $modal.open({
735                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-roles-confirm.html',
736                         controller: '',
737                         sizeClass: 'modal-medium', 
738                         resolve:'',
739                         scope: $scope
740                     })
741                 } else if(typeUpload === 'globalRoleFunctions'){
742                         $modal.open({
743                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-global-role-functions-confirm.html',
744                         controller: '',
745                         sizeClass: 'modal-medium', 
746                         resolve:'',
747                         scope: $scope
748                     })
749                 }
750              };
751
752              // Invoked by the Cancel button on the confirmation dialog.
753              $scope.cancelUpload = () => {
754                  ngDialog.close();
755              };
756              
757              init();
758         } // constructor
759     } // class
760     BulkRoleAndFunctionsModalCtrl.$inject = ['$scope', '$log', '$filter', '$q',  '$modalInstance', '$modal', 'ngDialog', 'message', 'confirmBoxService', 'usersService', 'applicationsService', 'functionalMenuService', 'RoleService'];    
761     angular.module('ecompApp').controller('BulkRoleAndFunctionsModalCtrl', BulkRoleAndFunctionsModalCtrl);
762
763     angular.module('ecompApp').directive('fileChange', ['$parse', function($parse){
764         return {
765                 require: 'ngModel',
766             restrict: 'A',
767             link : function($scope, element, attrs, ngModel) {
768                 var attrHandler = $parse(attrs['fileChange']);
769                 var handler=function(e) {
770                         $scope.$apply(function() {
771                                 attrHandler($scope, { $event:e, files:e.target.files } );
772                                 $scope.selectedFile = e.target.files[0].name;
773                         });
774                 };
775                 element[0].addEventListener('change',handler,false);
776            }
777         }
778     }]);
779     
780     angular.module('ecompApp').filter('csvToFuncObj',function() {
781         return function(input) {
782             var result = [];
783             var len, i, line, o;
784                 var lines = input.split('\n');
785             // Need 1-based index below
786             for (len = lines.length, i = 1; i <= len; ++i) {
787                 // Use 0-based index for array
788                 line = lines[i - 1].trim();
789                         if (line.length == 0) {
790                                 // console.log("Skipping blank line");
791                                 result.push({
792                                         line: i,
793                                         type: '',
794                                         instance: '',
795                                         action: '',
796                                         name: '',
797                                         status: 'Blank line'
798                                 });
799                                 continue;
800                         }
801                         o = line.split(',');
802                         if (o.length !== 4) {
803                                 // other lengths not valid for upload
804                                 result.push({
805                                         line: i,
806                                         type: o[0],
807                                         instance: o[1],
808                                         action: o[2],
809                                         name: '',
810                                         status: 'Failed to find 4 comma-separated values'
811                                 });
812                         }
813                         else {
814                                 // console.log("Valid line: ", val);
815                                 let entry = {
816                                                 line: i,
817                                                 type: o[0],
818                                                 instance: o[1],
819                                                 action: o[2],
820                                                 name: o[3]
821                                                 // leave status undefined, this
822                                                 // could be valid.
823                                 };
824                                 if (o[0].toLowerCase() === 'type') {
825                                         // not valid for upload, so set status
826                                         entry.status = 'Header';
827                                 }
828                                 else if (o[0].toLowerCase() === 'instance') {
829                                         // not valid for upload, so set status
830                                         entry.status = 'Header';
831                                 }
832                                 else if (o[0].toLowerCase() === 'action') {
833                                         // not valid for upload, so set status
834                                         entry.status = 'Header';
835                                 }
836                                 else if (o[0].toLowerCase() === 'name') {
837                                         // not valid for upload, so set status
838                                         entry.status = 'Header';
839                                 }
840                                 else if (o[0].trim() == '' || o[1].trim() == '' ||  o[2].trim() == '' ||  o[3].trim() == '') {
841                                         // defend against line with only a
842                                         // single comma etc.
843                                         entry.status = 'Failed to find non-empty values';                                       
844                                 }
845                                 result.push(entry);
846                         } // len 2
847             } // for
848             return result;
849         };
850     });
851     
852     angular.module('ecompApp').filter('csvToRoleFuncObj',function() {
853         return function(input) {
854             var result = [];
855             var len, i, line, o;
856                 var lines = input.split('\n');
857             // Need 1-based index below
858             for (len = lines.length, i = 1; i <= len; ++i) {
859                 // Use 0-based index for array
860                 line = lines[i - 1].trim();
861                         if (line.length == 0) {
862                                 // console.log("Skipping blank line");
863                                 result.push({
864                                         line: i,
865                                         role:'',
866                                         type: '',
867                                         instance: '',
868                                         action: '',
869                                         name: '',
870                                         status: 'Blank line'
871                                 });
872                                 continue;
873                         }
874                         o = line.split(',');
875                         if (o.length !== 5) {
876                                 // other lengths not valid for upload
877                                 result.push({
878                                         line: i,
879                                         role: o[0],
880                                         type: o[1],
881                                         instance: o[2],
882                                         action: o[3],
883                                         name: '',
884                                         status: 'Failed to find 4 comma-separated values'
885                                 });
886                         }
887                         else {
888                                 // console.log("Valid line: ", val);
889                                 let entry = {
890                                                 line: i,
891                                                 role: o[0],
892                                                 type: o[1],
893                                                 instance: o[2],
894                                                 action: o[3],
895                                                 name: o[4]
896                                                 // leave status undefined, this
897                                                 // could be valid.
898                                 };
899                                 if (o[0].toLowerCase() === 'role') {
900                                         // not valid for upload, so set status
901                                         entry.status = 'Header';
902                                 } else if (o[0].toLowerCase() === 'type') {
903                                         // not valid for upload, so set status
904                                         entry.status = 'Header';
905                                 }
906                                 else if (o[0].toLowerCase() === 'instance') {
907                                         // not valid for upload, so set status
908                                         entry.status = 'Header';
909                                 }
910                                 else if (o[0].toLowerCase() === 'action') {
911                                         // not valid for upload, so set status
912                                         entry.status = 'Header';
913                                 }
914                                 else if (o[0].toLowerCase() === 'name') {
915                                         // not valid for upload, so set status
916                                         entry.status = 'Header';
917                                 }
918                                 else if (o[0].trim() == '' || o[1].trim() == '' ||  o[2].trim() == '' ||  o[3].trim() == '' || o[4].trim() == '') {
919                                         // defend against line with only a
920                                         // single comma etc.
921                                         entry.status = 'Failed to find non-empty values';                                       
922                                 }
923                                 result.push(entry);
924                         } // len 2
925             } // for
926             return result;
927         };
928     });
929     
930     angular.module('ecompApp').filter('csvToRoleObj',function() {
931         return function(input) {
932             var result = [];
933             var len, i, line, o;
934                 var lines = input.split('\n');
935             // Need 1-based index below
936             for (len = lines.length, i = 1; i <= len; ++i) {
937                 // Use 0-based index for array
938                 line = lines[i - 1].trim();
939                         if (line.length == 0) {
940                                 // console.log("Skipping blank line");
941                                 result.push({
942                                         line: i,
943                                         role:'',
944                                         priority: '',
945                                         status: 'Blank line'
946                                 });
947                                 continue;
948                         }
949                         o = line.split(',');
950                         if (o.length === 0 && line.length !== 0) {
951                                 // other lengths not valid for upload
952                                 result.push({
953                                         line: i,
954                                         role: o[0],
955                                         priority:null
956                                 });
957                         }
958                         else {
959                                 // console.log("Valid line: ", val);
960                                 let entry = {
961                                                 line: i,
962                                                 role: o[0],
963                                                 priority: o[1]
964                                                 // leave status undefined, this
965                                                 // could be valid.
966                                 };
967                                 if (o[0].toLowerCase() === 'role') {
968                                         // not valid for upload, so set status
969                                         entry.status = 'Header';
970                                 }
971                                 if (o[0].toLowerCase() === 'priority') {
972                                         // not valid for upload, so set status
973                                         entry.status = 'Header';
974                                 }
975                                 else if (o[0].trim() == '') {
976                                         // defend against line with only a
977                                         // single comma etc.
978                                         entry.status = 'Failed to find non-empty values';                                       
979                                 }
980                                 result.push(entry);
981                         } // len 2
982             } // for
983             return result;
984         };
985     });
986     
987 })();