Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
feat(app): 2393 retry failed api calls once
Browse files Browse the repository at this point in the history
- waits between 1 to 5s before the retry
- displays a warning modal when retry fails
Closes #2393
  • Loading branch information
Christine Yu committed Jun 21, 2016
1 parent 1c1d8fd commit d824d18
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 24 deletions.
21 changes: 0 additions & 21 deletions app/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ function appRun(gettextCatalog: any,
UserService: IUserService,
ProjectsService: IProjectsService,
$window: ng.IWindowService,
$uibModal: any,
$uibModalStack,
LocalStorageService: ILocalStorageService
) {
Expand All @@ -135,26 +134,6 @@ function appRun(gettextCatalog: any,

$rootScope.config = config;
Restangular.addFullRequestInterceptor(addTokenToRequest);
Restangular.setErrorInterceptor((response) => {
CoreService.xhrDone();
if (response.status === 500 && !$uibModalStack.getTop()) {
$uibModal.open({
templateUrl: "core/templates/internal-server-error.html",
controller: "WarningController",
controllerAs: "wc",
backdrop: "static",
keyboard: false,
backdropClass: "warning-backdrop",
animation: false,
size: "lg",
resolve: {
warning: null
}
});
}
// TODO more than just 404
//$state.go("404", {}, {inherit: true});
});
Restangular.addResponseInterceptor((data, operation: string, model: string, url, response, deferred) => {
// Ajax
CoreService.xhrDone();
Expand Down
5 changes: 3 additions & 2 deletions app/scripts/components/user/user.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ module ngApp.components.user.services {
.then((data) => {
this.setUser(data);
}, (response) => {
if(response.status === 401) {
if (response && response.status === 401) {
if (this.currentUser) {
this.currentUser = undefined;
this.notify({
Expand All @@ -76,7 +76,8 @@ module ngApp.components.user.services {
});
}
} else {
this.$log.error("Error logging in, response status " + response.status);
const status = (response || {status: undefined}).status;
this.$log.error(`Error logging in, response status ${status}`);
}
})
.finally(() => this.isFetching = false);
Expand Down
47 changes: 47 additions & 0 deletions app/scripts/core/core.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,23 @@ module ngApp.core.services {
/* @ngInject */
constructor(private $rootScope: ngApp.IRootScope,
private $state: ng.ui.IStateService,
private $http: ng.IHttpService,
private Restangular: restangular.IProvider,
private config: IGDCConfig,
private ngProgressLite: ng.progressLite.INgProgressLite,
private $uibModal: any,
private $uibModalStack: any,
private Restangular: restangular.IProvider,
private gettextCatalog) {
this.setLoadedState(true);
Restangular.setErrorInterceptor((response, deferred, responseHandler) => {
this.xhrDone();
if (response.status >= 500) {
console.log(`${JSON.stringify(response.config)} failed with response.status`);
return this.retry(response, deferred, responseHandler);
}
return true;
});
}

setLoadedState(state: boolean) {
Expand Down Expand Up @@ -61,6 +75,39 @@ module ngApp.core.services {
this.$rootScope.modelLoaded = state;
}

retry(response: any, deferred: any, responseHandler: any) {
const r = () => {
this.$http(response.config)
.then(responseHandler,
(response) => {
if (response.config.url.indexOf(this.config.auth) !== -1) {
return;
}
if (!this.$uibModalStack.getTop()) {
response && this.$uibModal.open({
templateUrl: "core/templates/internal-server-error.html",
controller: "WarningController",
controllerAs: "wc",
backdrop: "static",
keyboard: false,
backdropClass: "warning-backdrop",
animation: false,
size: "lg",
resolve: {
warning: null
}
});
}
this.$rootScope.$emit('ClearLoadingScreen');
deferred.reject();
});
};
const timeOut = Math.floor((Math.random() * 5) + 1) * 1000;
console.log(`retrying in ${timeOut}ms`);
setTimeout(r, timeOut);
return false;
}

}

export interface ILocalStorageService {
Expand Down
87 changes: 87 additions & 0 deletions app/scripts/core/tests/core.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
describe('Core:', function () {

var CoreService,
FilesService,
ParticipantsService,
ProjectsService,
AnnotationsService,
httpBackend;

// Initialization of the AngularJS application before each test case
beforeEach(module('ngApp.files',
'ngApp.participants',
'ngApp.projects',
'ngApp.annotations',
'core.services'));

beforeEach(module(function ($provide) {
$provide.value('notify', { closeAll: function() {} });
$provide.value('config', {});
$provide.value('ngProgressLite', {});
$provide.value('AuthRestangular', {});
$provide.value('RestFullResponse', {});
}));

beforeEach(inject(function ($httpBackend) {
httpBackend = $httpBackend;
}));

describe('Service:', function () {

describe('should retry API calls once:', function () {
it('on Files:', inject(function (CoreService, FilesService) {
sinon.spy(FilesService.ds, 'get');
sinon.spy(CoreService, 'retry');

httpBackend.whenGET("/files?filters=%7B%7D&from=1&size=20&sort=file_name:asc").respond(500, '');

FilesService.getFiles();
httpBackend.flush();

expect(FilesService.ds.get).to.have.been.calledOnce;
expect(CoreService.retry).to.have.been.calledOnce;
}));

it('on Participants:', inject(function (CoreService, ParticipantsService) {
sinon.spy(ParticipantsService.ds, 'get');
sinon.spy(CoreService, 'retry');

httpBackend.whenGET("/cases?filters=%7B%7D&from=1&size=20&sort=case_id:asc").respond(500, '');

ParticipantsService.getParticipants();
httpBackend.flush();

expect(ParticipantsService.ds.get).to.have.been.calledOnce;
expect(CoreService.retry).to.have.been.calledOnce;
}));

it('on Projects:', inject(function (CoreService, ProjectsService) {
sinon.spy(ProjectsService.ds, 'get');
sinon.spy(CoreService, 'retry');

httpBackend.whenGET("/projects?filters=%7B%7D&from=1&size=20&sort=summary.case_count:desc").respond(500, '');

ProjectsService.getProjects();
httpBackend.flush();

expect(ProjectsService.ds.get).to.have.been.calledOnce;
expect(CoreService.retry).to.have.been.calledOnce;
}));

it('on Annotations:', inject(function (CoreService, AnnotationsService) {
sinon.spy(AnnotationsService.ds, 'get');
sinon.spy(CoreService, 'retry');

httpBackend.whenGET("/annotations?filters=%7B%7D&from=1&size=20&sort=entity_type:asc").respond(500, '');

AnnotationsService.getAnnotations();
httpBackend.flush();

expect(AnnotationsService.ds.get).to.have.been.calledOnce;
expect(CoreService.retry).to.have.been.calledOnce;
}));

});
});

});
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"moment": "~2.10.3",
"ngprogress-lite": "1.0.7",
"ng-tags-input": "2.3.0",
"restangular": "1.5.1",
"restangular": "1.5.2",
"ng-table": "0.5.4",
"ng-sortable": "~1.2.2",
"bowser": "1.0.0"
Expand Down
5 changes: 5 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ module.exports = function (config) {
'bower_components/angular-notify/angular-notify.js',
'bower_components/angular-cookies/angular-cookies.js',
'bower_components/angular-drag-and-drop-lists/angular-drag-and-drop-lists.js',
'bower_components/angular-animate/angular-animate.js',
'bower_components/angular-aria/angular-aria.js',
'bower_components/angular-sanitize/angular-sanitize.js',
'bower_components/ng-tags-input/ng-tags-input.js',
'bower_components/ng-sortable/dist/ng-sortable.js',
'.tmp/scripts/**/*.js',
'app/scripts/**/*.tests.js',
'app/scripts/**/tests/*.js'
Expand Down

0 comments on commit d824d18

Please sign in to comment.