From 54ff59af77c9a613b2e29b36e6d6d0fcbbbc4075 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 15:40:43 +0100 Subject: [PATCH 01/11] Update MainModule.js --- src/javascripts/ng-admin/Main/MainModule.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/javascripts/ng-admin/Main/MainModule.js b/src/javascripts/ng-admin/Main/MainModule.js index 78930fab..6f4f2a68 100644 --- a/src/javascripts/ng-admin/Main/MainModule.js +++ b/src/javascripts/ng-admin/Main/MainModule.js @@ -8,6 +8,7 @@ var MainModule = angular.module('main', ['ui.router', 'restangular', 'pascalprec MainModule.controller('AppController', require('./component/controller/AppController')); MainModule.controller('DashboardController', require('./component/controller/DashboardController')); MainModule.provider('NgAdminConfiguration', require('./component/provider/NgAdminConfiguration')); +MainModule.factory('httpErrorService', require('./component/provider/HttpErrorService')); MainModule.filter('orderElement', require('./component/filter/OrderElement')); MainModule.filter('stripTags', require('./component/filter/StripTags')); From 8148b30a8ec8ca35ca3284dc7c294cc2c46532fc Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 15:41:14 +0100 Subject: [PATCH 02/11] Update translate.js --- src/javascripts/ng-admin/Main/config/translate.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/javascripts/ng-admin/Main/config/translate.js b/src/javascripts/ng-admin/Main/config/translate.js index 656697b9..f1e6ac5c 100644 --- a/src/javascripts/ng-admin/Main/config/translate.js +++ b/src/javascripts/ng-admin/Main/config/translate.js @@ -38,6 +38,7 @@ export default function translate($translateProvider) { 'NEXT': 'Next ยป', 'DETAIL': 'Detail', 'STATE_CHANGE_ERROR': 'State change error: {{ message }}', + 'STATE_FORBIDDEN_ERROR': 'A server 403 error occured: {{ message }}', 'NOT_FOUND': 'Not Found', 'NOT_FOUND_DETAILS': 'The page you are looking for cannot be found. Take a break before trying again.', }); From 626b509cc1ad7c457acfa8c9fea2c19f950a3ff9 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 15:44:37 +0100 Subject: [PATCH 03/11] Create HttpErrorService.js --- .../component/provider/HttpErrorService.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js diff --git a/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js new file mode 100644 index 00000000..7fd0bc30 --- /dev/null +++ b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js @@ -0,0 +1,33 @@ +export default function httpErrorService($state, $translate, notification) { + + return { + handleError: function(event, toState, toParams, fromState, fromParams, error){ + switch (error.status) { + case 404: + this.handle404Error(); + break; + case 403: + this.handle403Error(error); + break; + default: + this.handleDefaultError(error); + break; + } + }, + handle404Error: function(event, error){ + $state.go('ma-404'); + event.preventDefault(); + }, + handle403Error: function(error){ + $translate('STATE_FORBIDDEN_ERROR', { message: error.data.message }).then(this.displayError); + throw error; + }, + handleDefaultError: function(error){ + $translate('STATE_CHANGE_ERROR', { message: error.data.message }).then(this.displayError); + throw error; + }, + displayError:text => notification.log(text, { addnCls: 'humane-flatty-error' }) + } +} + +httpErrorService.$inject =['$state', '$translate', 'notification']; From b0669200a15f2db69e00fd5b74a438c2c06d6664 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 16:01:38 +0100 Subject: [PATCH 04/11] Update and rename ErrorHandler.js to HttpErrorHandler.js --- src/javascripts/ng-admin/Main/run/ErrorHandler.js | 13 ------------- .../ng-admin/Main/run/HttpErrorHandler.js | 7 +++++++ 2 files changed, 7 insertions(+), 13 deletions(-) delete mode 100644 src/javascripts/ng-admin/Main/run/ErrorHandler.js create mode 100644 src/javascripts/ng-admin/Main/run/HttpErrorHandler.js diff --git a/src/javascripts/ng-admin/Main/run/ErrorHandler.js b/src/javascripts/ng-admin/Main/run/ErrorHandler.js deleted file mode 100644 index 84eda538..00000000 --- a/src/javascripts/ng-admin/Main/run/ErrorHandler.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function errorHandler($rootScope, $state, $translate, notification) { - $rootScope.$on("$stateChangeError", function handleError(event, toState, toParams, fromState, fromParams, error) { - if (error.status == 404) { - $state.go('ma-404'); - event.preventDefault(); - } else { - $translate('STATE_CHANGE_ERROR', { message: error.message }).then(text => notification.log(text, { addnCls: 'humane-flatty-error' })); - throw error; - } - }); -} - -errorHandler.$inject = ['$rootScope', '$state', '$translate', 'notification']; diff --git a/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js b/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js new file mode 100644 index 00000000..c188deef --- /dev/null +++ b/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js @@ -0,0 +1,7 @@ +export default function httpErrorHandler($rootScope, HttpErrorService) { + $rootScope.$on("$stateChangeError", function handleError(event, toState, toParams, fromState, fromParams, error) { + HttpErrorService.handleError(event, toState, toParams, fromState, fromParams, error); + }); +} + +httpErrorHandler.$inject = ['$rootScope','httpErrorService']; From 6db5c58adbcf22d1db2242caaa7cb02577d6c4f1 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 16:02:01 +0100 Subject: [PATCH 05/11] Update MainModule.js --- src/javascripts/ng-admin/Main/MainModule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/javascripts/ng-admin/Main/MainModule.js b/src/javascripts/ng-admin/Main/MainModule.js index 6f4f2a68..c2136782 100644 --- a/src/javascripts/ng-admin/Main/MainModule.js +++ b/src/javascripts/ng-admin/Main/MainModule.js @@ -20,5 +20,5 @@ MainModule.config(require('./config/http')); MainModule.config(require('./config/routing')); MainModule.config(require('./config/translate')); -MainModule.run(require('./run/ErrorHandler')); +MainModule.run(require('./run/HttpErrorHandler')); MainModule.run(require('./run/Loader')); From d5e07d236ddc04b63859bdd8408e0df5af1bd872 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 16:11:24 +0100 Subject: [PATCH 06/11] Update Theming.md --- doc/Theming.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/Theming.md b/doc/Theming.md index a1633742..e1af0c65 100644 --- a/doc/Theming.md +++ b/doc/Theming.md @@ -181,3 +181,28 @@ entity.errorMessage(function (response) { return 'Global error: ' + response.status + '(' + response.data + ')'; }); ``` +## Customizing HTTP Error Messages +If you want to override, patch or extend the way HTTP errors are handled by the ng-admin [Http Error Service](../src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js) then you may use an [Angular decorator](https://docs.angularjs.org/guide/decorators), as per the below example: + +Create a decorator, HttpErrorDecorator.js +```js +// Change HTTP 403 error notification to display them as information +// and not errors ('humane-flatty-info' instead of 'humane-flatty-error') +export default function httpErrorDecorator($delegate, $translate, notification) { + + function handle403Error(error){ + $translate('STATE_FORBIDDEN_ERROR', {message: error.data.message}).then(text => notification.log(text, {addnCls: 'humane-flatty-info'})); + throw error; + } + + $delegate.handle403Error = handle403Error; + + return $delegate; +} + +httpErrorDecorator.$inject = ['$delegate', '$translate', 'notification']; +``` +Bind the decorator to your application: +```js +myApp.decorator('httpErrorService',require('HttpErrorDecorator')); +``` From 8ee451420b81446c305eb994df531e153995ef2e Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 18:25:09 +0100 Subject: [PATCH 07/11] Create HttpErrorServiceTest.spec.js --- .../provider/HttpErrorServiceTest.spec.js | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js diff --git a/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js new file mode 100644 index 00000000..f85c04e3 --- /dev/null +++ b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js @@ -0,0 +1,79 @@ +const HttpErrorService = require('../../../../../ng-admin/Main/component/provider/HttpErrorService'); + +describe('Http Error Service', () => { + let $state; + let $translate; + let httpErrorService; + let notification; + + angular.module('test_httpErrorService', []) + .service('$state', () => ({ + go: jasmine.createSpy('$state.go'), + })) + .service('$translate', () => ( + jasmine.createSpy('$translate').and.returnValue({ then: cb => cb('translated') }) + )) + .service('notification', () => ({ + log: jasmine.createSpy('notification.log'), + })) + .provider('HttpErrorService', HttpErrorService); + + beforeEach(angular.mock.module('test_httpErrorService')); + + beforeEach(inject(function (_$state_, _$translate_, _HttpErrorService_, _notification_) { + $state = _$state_; + $translate = _$translate_; + httpErrorService = _HttpErrorService_; + notification = _notification_; + })); + + it('should redirect to `ma-404` state when receiving a 404 error', () => { + const event = { + preventDefault: jasmine.createSpy(), + }; + + const error = { status: 404 }; + httpErrorService.handleError(event, '', '', '', '', error); + + expect(event.preventDefault).toHaveBeenCalled(); + expect($state.go).toHaveBeenCalledWith('ma-404'); + }); + + it('should display a translated forbidden error notification in case of 403', () => { + const error = { + status: 403, + data: { + message: 'Forbidden access', + }, + }; + + try { + httpErrorService.handleError('', '', '', '', '', error); + expect(true).toBe(false); + } catch (e) { + // should throw an exception in case of 403 + } + + expect($translate).toHaveBeenCalledWith('STATE_FORBIDDEN_ERROR', { message: 'Forbidden access' }); + expect(notification.log).toHaveBeenCalledWith('translated', { addnCls: 'humane-flatty-error' }); + }); + + it('should display generic translated error notification if neither 404 nor 403', () => { + const error = { + status: 500, + data: { + message: 'Unknown error', + } + }; + + try { + httpErrorService.handleError('', '', '', '', '', error); + expect(true).toBe(false); + } catch (e) { + // should throw an exception in case of 500 + } + + expect($translate).toHaveBeenCalledWith('STATE_CHANGE_ERROR', { message: 'Unknown error' }); + expect(notification.log).toHaveBeenCalledWith('translated', { addnCls: 'humane-flatty-error' }); + }); +}); From 5125c318a81db4e64cb1d0a542ab51d571a2746c Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 18:25:28 +0100 Subject: [PATCH 08/11] Update MainModule.js --- src/javascripts/ng-admin/Main/MainModule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/javascripts/ng-admin/Main/MainModule.js b/src/javascripts/ng-admin/Main/MainModule.js index c2136782..770f2bf9 100644 --- a/src/javascripts/ng-admin/Main/MainModule.js +++ b/src/javascripts/ng-admin/Main/MainModule.js @@ -8,7 +8,7 @@ var MainModule = angular.module('main', ['ui.router', 'restangular', 'pascalprec MainModule.controller('AppController', require('./component/controller/AppController')); MainModule.controller('DashboardController', require('./component/controller/DashboardController')); MainModule.provider('NgAdminConfiguration', require('./component/provider/NgAdminConfiguration')); -MainModule.factory('httpErrorService', require('./component/provider/HttpErrorService')); +MainModule.provider('httpErrorService', require('./component/provider/HttpErrorService')); MainModule.filter('orderElement', require('./component/filter/OrderElement')); MainModule.filter('stripTags', require('./component/filter/StripTags')); From 3588e0e2c8d8a1942b0c9461ea902bd0a2a16ced Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 9 Dec 2016 18:30:03 +0100 Subject: [PATCH 09/11] Update HttpErrorService.js --- .../component/provider/HttpErrorService.js | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js index 7fd0bc30..ef00bded 100644 --- a/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js +++ b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js @@ -1,33 +1,36 @@ -export default function httpErrorService($state, $translate, notification) { - - return { - handleError: function(event, toState, toParams, fromState, fromParams, error){ - switch (error.status) { +const httpErrorService = ($state, $translate, notification) => ({ + handleError: function(event, toState, toParams, fromState, fromParams, error) { + switch (error.status) { case 404: - this.handle404Error(); + this.handle404Error(event, error); break; case 403: this.handle403Error(error); break; default: this.handleDefaultError(error); - break; - } - }, - handle404Error: function(event, error){ - $state.go('ma-404'); - event.preventDefault(); - }, - handle403Error: function(error){ - $translate('STATE_FORBIDDEN_ERROR', { message: error.data.message }).then(this.displayError); - throw error; - }, - handleDefaultError: function(error){ - $translate('STATE_CHANGE_ERROR', { message: error.data.message }).then(this.displayError); - throw error; - }, - displayError:text => notification.log(text, { addnCls: 'humane-flatty-error' }) - } -} + break; + } + }, + + handle404Error: function(event) { + event.preventDefault(); + $state.go('ma-404'); + }, + + handle403Error: function(error) { + $translate('STATE_FORBIDDEN_ERROR', { message: error.data.message }).then(this.displayError); + throw error; + }, + + handleDefaultError: function(error) { + $translate('STATE_CHANGE_ERROR', { message: error.data.message }).then(this.displayError); + throw error; + }, + + displayError: text => notification.log(text, { addnCls: 'humane-flatty-error' }), +}); + +httpErrorService.$inject = ['$state', '$translate', 'notification']; -httpErrorService.$inject =['$state', '$translate', 'notification']; +export default { $get: httpErrorService }; From 2b03d41c672116c86dc7bef4085b1faec8c805c6 Mon Sep 17 00:00:00 2001 From: Jonathan Petitcolas Date: Mon, 19 Dec 2016 20:01:45 +0100 Subject: [PATCH 10/11] Minor fixes --- doc/Theming.md | 31 ++++++++++++------- src/javascripts/ng-admin/Main/MainModule.js | 2 +- .../component/provider/HttpErrorService.js | 6 ++-- .../ng-admin/Main/run/HttpErrorHandler.js | 6 ++-- .../provider/HttpErrorServiceTest.spec.js | 14 ++++----- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/doc/Theming.md b/doc/Theming.md index e1af0c65..e7de5f91 100644 --- a/doc/Theming.md +++ b/doc/Theming.md @@ -182,27 +182,36 @@ entity.errorMessage(function (response) { }); ``` ## Customizing HTTP Error Messages + If you want to override, patch or extend the way HTTP errors are handled by the ng-admin [Http Error Service](../src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js) then you may use an [Angular decorator](https://docs.angularjs.org/guide/decorators), as per the below example: Create a decorator, HttpErrorDecorator.js + ```js // Change HTTP 403 error notification to display them as information // and not errors ('humane-flatty-info' instead of 'humane-flatty-error') -export default function httpErrorDecorator($delegate, $translate, notification) { - - function handle403Error(error){ - $translate('STATE_FORBIDDEN_ERROR', {message: error.data.message}).then(text => notification.log(text, {addnCls: 'humane-flatty-info'})); - throw error; - } - - $delegate.handle403Error = handle403Error; - +export const HttpErrorDecorator = ($delegate, $translate, notification) => { + $delegate.handle403Error = error => { + $translate('STATE_FORBIDDEN_ERROR', { + message: error.data.message, + }).then(text => notification.log(text, { + addnCls: 'humane-flatty-info', + })); + + throw error; + }; + return $delegate; } -httpErrorDecorator.$inject = ['$delegate', '$translate', 'notification']; +HttpErrorDecorator.$inject = ['$delegate', '$translate', 'notification']; + +export default HttpErrorDecorator; + ``` + Bind the decorator to your application: + ```js -myApp.decorator('httpErrorService',require('HttpErrorDecorator')); +myApp.decorator('HttpErrorService',require('HttpErrorDecorator')); ``` diff --git a/src/javascripts/ng-admin/Main/MainModule.js b/src/javascripts/ng-admin/Main/MainModule.js index 770f2bf9..c56752b6 100644 --- a/src/javascripts/ng-admin/Main/MainModule.js +++ b/src/javascripts/ng-admin/Main/MainModule.js @@ -8,7 +8,7 @@ var MainModule = angular.module('main', ['ui.router', 'restangular', 'pascalprec MainModule.controller('AppController', require('./component/controller/AppController')); MainModule.controller('DashboardController', require('./component/controller/DashboardController')); MainModule.provider('NgAdminConfiguration', require('./component/provider/NgAdminConfiguration')); -MainModule.provider('httpErrorService', require('./component/provider/HttpErrorService')); +MainModule.provider('HttpErrorService', require('./component/provider/HttpErrorService')); MainModule.filter('orderElement', require('./component/filter/OrderElement')); MainModule.filter('stripTags', require('./component/filter/StripTags')); diff --git a/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js index ef00bded..109a4238 100644 --- a/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js +++ b/src/javascripts/ng-admin/Main/component/provider/HttpErrorService.js @@ -1,4 +1,4 @@ -const httpErrorService = ($state, $translate, notification) => ({ +const HttpErrorService = ($state, $translate, notification) => ({ handleError: function(event, toState, toParams, fromState, fromParams, error) { switch (error.status) { case 404: @@ -31,6 +31,6 @@ const httpErrorService = ($state, $translate, notification) => ({ displayError: text => notification.log(text, { addnCls: 'humane-flatty-error' }), }); -httpErrorService.$inject = ['$state', '$translate', 'notification']; +HttpErrorService.$inject = ['$state', '$translate', 'notification']; -export default { $get: httpErrorService }; +export default { $get: HttpErrorService }; diff --git a/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js b/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js index c188deef..943462cd 100644 --- a/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js +++ b/src/javascripts/ng-admin/Main/run/HttpErrorHandler.js @@ -1,7 +1,7 @@ -export default function httpErrorHandler($rootScope, HttpErrorService) { - $rootScope.$on("$stateChangeError", function handleError(event, toState, toParams, fromState, fromParams, error) { +export default function HttpErrorHandler($rootScope, HttpErrorService) { + $rootScope.$on("$stateChangeError", (event, toState, toParams, fromState, fromParams, error) => { HttpErrorService.handleError(event, toState, toParams, fromState, fromParams, error); }); } -httpErrorHandler.$inject = ['$rootScope','httpErrorService']; +HttpErrorHandler.$inject = ['$rootScope', 'HttpErrorService']; diff --git a/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js index f85c04e3..d2e9ec5d 100644 --- a/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js +++ b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js @@ -3,10 +3,10 @@ const HttpErrorService = require('../../../../../ng-admin/Main/component/provide describe('Http Error Service', () => { let $state; let $translate; - let httpErrorService; + let HttpErrorService; let notification; - angular.module('test_httpErrorService', []) + angular.module('test_HttpErrorService', []) .service('$state', () => ({ go: jasmine.createSpy('$state.go'), })) @@ -18,12 +18,12 @@ describe('Http Error Service', () => { })) .provider('HttpErrorService', HttpErrorService); - beforeEach(angular.mock.module('test_httpErrorService')); + beforeEach(angular.mock.module('test_HttpErrorService')); beforeEach(inject(function (_$state_, _$translate_, _HttpErrorService_, _notification_) { $state = _$state_; $translate = _$translate_; - httpErrorService = _HttpErrorService_; + HttpErrorService = _HttpErrorService_; notification = _notification_; })); @@ -33,7 +33,7 @@ describe('Http Error Service', () => { }; const error = { status: 404 }; - httpErrorService.handleError(event, '', '', '', '', error); + HttpErrorService.handleError(event, '', '', '', '', error); expect(event.preventDefault).toHaveBeenCalled(); expect($state.go).toHaveBeenCalledWith('ma-404'); @@ -48,7 +48,7 @@ describe('Http Error Service', () => { }; try { - httpErrorService.handleError('', '', '', '', '', error); + HttpErrorService.handleError('', '', '', '', '', error); expect(true).toBe(false); } catch (e) { // should throw an exception in case of 403 @@ -67,7 +67,7 @@ describe('Http Error Service', () => { }; try { - httpErrorService.handleError('', '', '', '', '', error); + HttpErrorService.handleError('', '', '', '', '', error); expect(true).toBe(false); } catch (e) { // should throw an exception in case of 500 From ece3ae5a4d3e740dd296458b9497436e540e4557 Mon Sep 17 00:00:00 2001 From: Jonathan Petitcolas Date: Tue, 20 Dec 2016 08:20:20 +0100 Subject: [PATCH 11/11] Fix tests --- .../unit/Main/component/provider/HttpErrorServiceTest.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js index d2e9ec5d..1ab71d50 100644 --- a/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js +++ b/src/javascripts/test/unit/Main/component/provider/HttpErrorServiceTest.spec.js @@ -1,4 +1,4 @@ -const HttpErrorService = require('../../../../../ng-admin/Main/component/provider/HttpErrorService'); +const HttpErrorServiceProvider = require('../../../../../ng-admin/Main/component/provider/HttpErrorService'); describe('Http Error Service', () => { let $state; @@ -16,7 +16,7 @@ describe('Http Error Service', () => { .service('notification', () => ({ log: jasmine.createSpy('notification.log'), })) - .provider('HttpErrorService', HttpErrorService); + .provider('HttpErrorService', HttpErrorServiceProvider); beforeEach(angular.mock.module('test_HttpErrorService'));