Merge "Added error msg when gets redirect to login page"

This commit is contained in:
Zuul 2018-01-10 16:15:19 +00:00 committed by Gerrit Code Review
commit ba01268334
6 changed files with 221 additions and 28 deletions

View File

@ -22,7 +22,11 @@
'horizon.framework.widgets'
])
.config(config)
.run(run);
.run(run)
.factory('horizon.framework.redirect', httpRedirectLogin)
.constant('horizon.framework.events', {
FORCE_LOGOUT: 'FORCE_LOGOUT'
});
config.$inject = [
'$injector',
@ -70,23 +74,9 @@
// Global http error handler
// if user is not authorized, log user out
// this can happen when session expires
$httpProvider.interceptors.push(redirect);
$httpProvider.interceptors.push(httpRedirectLogin);
$httpProvider.interceptors.push(stripAjaxHeaderForCORS);
redirect.$inject = ['$q'];
function redirect($q) {
return {
responseError: function (error) {
if (error.status === 401) {
var $window = $windowProvider.$get();
$window.location.replace($window.WEBROOT + 'auth/logout');
}
return $q.reject(error);
}
};
}
stripAjaxHeaderForCORS.$inject = [];
// Standard CORS middleware used in OpenStack services doesn't expect
// X-Requested-With header to be set for requests and rejects requests
@ -125,4 +115,38 @@
}
}
httpRedirectLogin.$inject = [
'$q',
'$rootScope',
'$window',
'horizon.framework.events',
'horizon.framework.widgets.toast.service'
];
function httpRedirectLogin($q, $rootScope, $window, frameworkEvents, toastService) {
return {
responseError: function (error) {
if (error.status === 401) {
var msg = gettext('Unauthorized. Redirecting to login');
handleRedirectMessage(msg, $rootScope, $window, frameworkEvents, toastService);
}
if (error.status === 403) {
var msg2 = gettext('Forbidden. Redirecting to login');
handleRedirectMessage(msg2, $rootScope, $window, frameworkEvents, toastService);
}
return $q.reject(error);
}
};
}
function handleRedirectMessage(msg, $rootScope, $window, frameworkEvents, toastService) {
var toast = toastService.find('error', msg);
//Suppress the multiple duplicate redirect toast messages.
if (!toast) {
toastService.add('error', msg);
$rootScope.$broadcast(frameworkEvents.FORCE_LOGOUT, msg);
}
$window.location.replace($window.WEBROOT + 'auth/logout');
}
})();

View File

@ -35,15 +35,45 @@
}));
describe('when unauthorized', function() {
it('should redirect to /auth/logout', inject(function($http, $httpBackend, $window) {
$window.WEBROOT = '/dashboard/';
$httpBackend.when('GET', '/api').respond(401, '');
it('should redirect to /auth/logout and add an unauthorized toast message ', inject(
function($http, $httpBackend, $window, $injector, $rootScope) {
$window.WEBROOT = '/dashboard/';
$httpBackend.when('GET', '/api').respond(401, '');
$http.get('/api').error(function() {
expect($window.location.replace).toHaveBeenCalledWith('/dashboard/auth/logout');
});
$httpBackend.flush();
}));
var toastService = $injector.get('horizon.framework.widgets.toast.service');
spyOn(toastService, 'add');
spyOn($rootScope, '$broadcast').and.callThrough();
$http.get('/api').error(function() {
expect(toastService.add).toHaveBeenCalled();
expect($rootScope.$broadcast).toHaveBeenCalled();
expect($window.location.replace).toHaveBeenCalledWith('/dashboard/auth/logout');
});
$httpBackend.flush();
})
);
});
describe('when forbidden', function() {
it('should redirect to /auth/logout and add a forbidden toast message ', inject(
function($http, $httpBackend, $window, $injector, $rootScope) {
$window.WEBROOT = '/dashboard/';
$httpBackend.when('GET', '/api').respond(403, '');
var toastService = $injector.get('horizon.framework.widgets.toast.service');
spyOn(toastService, 'add');
spyOn($rootScope, '$broadcast').and.callThrough();
$http.get('/api').error(function() {
expect(toastService.add).toHaveBeenCalled();
expect($rootScope.$broadcast).toHaveBeenCalled();
expect($window.location.replace).toHaveBeenCalledWith('/dashboard/auth/logout');
});
$httpBackend.flush();
})
);
});
});
})();

View File

@ -45,6 +45,7 @@
types: {},
add: add,
get: get,
find: find,
cancel: cancel,
clearAll: clearAll,
clearErrors: clearErrors,
@ -118,6 +119,20 @@
return toasts;
}
/**
* find a matching existing toast based on type and message
*
* @param type type of the message
* @param msg localized message of the toast
* @returns {*} return toast object if find matching one
*/
function find(type, msg) {
return toasts.find(function(toast) {
var toastType = (type === 'error' ? 'danger' : type);
return (toast.type === toastType && toast.msg.localeCompare(msg) === 0);
});
}
/**
* Remove all toasts.
*/

View File

@ -67,6 +67,18 @@
expect(service.get()[0].type).toBe('danger');
});
it('should find the added toast message', function() {
service.add('error', dangerMsg);
var toast = service.find('error', dangerMsg);
expect(toast.type).toBe('danger');
expect(toast.msg).toBe(dangerMsg);
service.add('success', successMsg);
toast = service.find('success', successMsg);
expect(toast.type).toBe('success');
expect(toast.msg).toBe(successMsg);
});
it('should provide a function to clear all toasts', function() {
service.add('success', successMsg);
service.add('success', successMsg);

View File

@ -27,7 +27,8 @@
'$scope',
'$q',
'horizon.framework.widgets.wizard.labels',
'horizon.framework.widgets.wizard.events'
'horizon.framework.widgets.wizard.events',
'horizon.framework.events'
];
/**
@ -36,7 +37,8 @@
* @description
* Controller used by 'wizard'
*/
function WizardController($scope, $q, wizardLabels, wizardEvents) {
function WizardController($scope, $q, wizardLabels, wizardEvents, frameworkEvents) {
var ctrl = this;
var viewModel = $scope.viewModel = {};
var initTask = $q.defer();
@ -55,6 +57,10 @@
$scope.switchTo = switchTo;
$scope.showError = showError;
ctrl.toggleHelpBtn = toggleHelpBtn;
ctrl.onInitSuccess = onInitSuccess;
ctrl.onInitError = onInitError;
/*eslint-enable angular/controller-as */
viewModel.btnText = extend({}, wizardLabels, $scope.workflow.btnText);
@ -85,7 +91,7 @@
from: $scope.currentIndex,
to: index
});
toggleHelpBtn(index);
ctrl.toggleHelpBtn(index);
/*eslint-disable angular/controller-as */
$scope.currentIndex = index;
$scope.openHelp = false;
@ -122,9 +128,13 @@
}
function onInitSuccess() {
if (viewModel.hasError) {
return;
}
$scope.$broadcast(wizardEvents.ON_INIT_SUCCESS);
if (steps.length > 0) {
toggleHelpBtn(0);
ctrl.toggleHelpBtn(0);
}
}
@ -132,6 +142,12 @@
$scope.$broadcast(wizardEvents.ON_INIT_ERROR);
}
$scope.$on(frameworkEvents.FORCE_LOGOUT, function(evt, arg) {
viewModel.hasError = true;
viewModel.errorMessage = arg;
return;
});
function toggleHelpBtn(index) {
// Toggle help icon button if a step's helpUrl is not defined
if (angular.isUndefined(steps[index].helpUrl)) {

View File

@ -0,0 +1,96 @@
/*
* (c) Copyright 2017 SUSE Linux
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
describe("WizardController", function() {
var ctrl, scope, wizardLabels, wizardEvents, frameworkEvents, rootScope;
beforeEach(module('horizon.framework'));
beforeEach(inject(function($controller, $rootScope, $injector, $q) {
scope = $rootScope.$new();
rootScope = $rootScope;
wizardLabels = $injector.get('horizon.framework.widgets.wizard.labels');
wizardEvents = $injector.get('horizon.framework.widgets.wizard.events');
frameworkEvents = $injector.get('horizon.framework.events');
ctrl = $controller('WizardController', {
$scope: scope,
$q: $q,
wizardLabels: wizardLabels,
wizardEvents: wizardEvents,
frameworkEvents: frameworkEvents
});
scope.$apply();
}));
it('is defined', function() {
expect(ctrl).toBeDefined();
});
it('viewModel is defined', function() {
expect(scope.viewModel).toBeDefined();
});
it('call switchTo', function() {
spyOn(ctrl, 'toggleHelpBtn');
spyOn(scope, '$broadcast');
scope.switchTo(1);
scope.$apply();
expect(ctrl.toggleHelpBtn).toHaveBeenCalled();
expect(scope.currentIndex).toBe(1);
expect(scope.openHelp).toBe(false);
expect(scope.$broadcast).toHaveBeenCalled();
});
it('call showError', function() {
spyOn(scope, 'showError').and.callThrough();
scope.showError('in valid');
scope.$apply();
expect(scope.viewModel.hasError).toBe(true);
expect(scope.viewModel.errorMessage).toBe('in valid');
});
it('call onInitSuccess with logout event', function() {
rootScope.$broadcast(frameworkEvents.FORCE_LOGOUT, 'logout');
ctrl.onInitSuccess();
scope.$apply();
expect(scope.viewModel.hasError).toBe(true);
});
it('call onInitSuccess without logout event', function() {
spyOn(scope, '$broadcast');
ctrl.onInitSuccess();
scope.$apply();
expect(scope.viewModel.hasError).toBe(false);
expect(scope.$broadcast).toHaveBeenCalledWith(wizardEvents.ON_INIT_SUCCESS);
});
it('call onInitError with logout event', function() {
rootScope.$broadcast(frameworkEvents.FORCE_LOGOUT, 'logout');
ctrl.onInitError();
scope.$apply();
expect(scope.viewModel.hasError).toBe(true);
});
it('call onInitError without logout event', function() {
spyOn(scope, '$broadcast');
ctrl.onInitError();
scope.$apply();
expect(scope.viewModel.hasError).toBe(false);
expect(scope.$broadcast).toHaveBeenCalledWith(wizardEvents.ON_INIT_ERROR);
});
});
})();