From d9cafc686e54b49b0922a4670d7372f0e45d86b4 Mon Sep 17 00:00:00 2001
From: jaden <noreply@example.com>
Date: Wed, 24 May 2017 16:13:49 +0200
Subject: [PATCH] add errorService to handle user-facing errors

---
 .../static/reports/app/directives/error.js    | 78 +++++++++++++++++++
 .../static/reports/app/directives/lock.js     |  9 ++-
 .../static/reports/app/directives/publish.js  |  6 +-
 .../static/reports/app/directives/save.js     |  5 +-
 .../reports/app/services/errorService.js      | 71 +++++++++++++++++
 .../web/reports/templates/reports/report.html |  2 +
 6 files changed, 165 insertions(+), 6 deletions(-)
 create mode 100644 beat/web/reports/static/reports/app/directives/error.js
 create mode 100644 beat/web/reports/static/reports/app/services/errorService.js

diff --git a/beat/web/reports/static/reports/app/directives/error.js b/beat/web/reports/static/reports/app/directives/error.js
new file mode 100644
index 000000000..673449b70
--- /dev/null
+++ b/beat/web/reports/static/reports/app/directives/error.js
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ *
+ * This file is part of the beat.web module of the BEAT platform.
+ *
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ *
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+ */
+
+/*
+ * reportError
+ * Desc:
+ * 	Watches the ErrorService's error list and processes an error if
+ * 	its not empty.
+ * 	'Processing': showing a modal to the user about the error.
+ *
+ */
+angular.module('reportApp')
+.directive("reportError", ['ErrorService', '$timeout', function(ErrorService, $timeout){
+	return {
+		scope: {
+		},
+		restrict: 'E',
+		link: function(scope){
+			scope.error = null;
+
+			const processError = () => {
+				// if theres no error, return
+				if(!ErrorService._hasError){
+					return;
+				}
+
+				// save our error
+				scope.error = ErrorService._getError();
+
+				// pop up the modal
+				$('#errorReportModal').modal();
+			};
+
+			$timeout(() => {
+				$('#errorReportModal').on('hidden.bs.modal', () => {
+					// finished processing the last error
+					scope.error = null;
+
+					// process the next one, if there is one
+					processError();
+				});
+			}, 0);
+
+			scope.$watch(ErrorService._hasError, processError);
+		},
+		template: `
+<bootstrap-modal dom-id='errorReportModal' button-cancel-text='Continue'>
+	<b-title>
+		Error
+	</b-title>
+	<b-content>
+		<p>There was an error:</p>
+		<p>{{ error.message }}</p>
+		<pre>{{ error.error.message }}</pre>
+	</b-content>
+</bootstrap-modal>
+`
+	};
+}]);
diff --git a/beat/web/reports/static/reports/app/directives/lock.js b/beat/web/reports/static/reports/app/directives/lock.js
index ad02cd8d8..804acc148 100644
--- a/beat/web/reports/static/reports/app/directives/lock.js
+++ b/beat/web/reports/static/reports/app/directives/lock.js
@@ -26,14 +26,19 @@
  * 	Displays a modal for locking the current report.
  */
 angular.module('reportApp')
-.directive("reportLock", ['ReportService', function(ReportService){
+.directive("reportLock", ['ReportService', 'ErrorService', function(ReportService, ErrorService){
 	return {
 		scope: {
 		},
 		restrict: 'E',
 		link: function(scope, el){
 			// sends the request to lock the report
-			scope.lockReport = ReportService.lockReport;
+			scope.lockReport = () => {
+				return ReportService.lockReport()
+				.catch(e, () => {
+					ErrorService.logError(e, `Could not lock the report.`);
+				});
+			}
 		},
 		template: `
 <bootstrap-modal dom-id='lockReportModal' button-submit-text='Lock' button-submit-func='lockReport'>
diff --git a/beat/web/reports/static/reports/app/directives/publish.js b/beat/web/reports/static/reports/app/directives/publish.js
index 4d20ca09b..8833a3445 100644
--- a/beat/web/reports/static/reports/app/directives/publish.js
+++ b/beat/web/reports/static/reports/app/directives/publish.js
@@ -28,7 +28,7 @@
  * 	Visible or Public.
  */
 angular.module('reportApp')
-.directive("reportPublish", ['reportFactory', 'ReportService', '$timeout', function(reportFactory, ReportService, $timeout){
+.directive("reportPublish", ['reportFactory', 'ReportService', '$timeout', 'ErrorService', function(reportFactory, ReportService, $timeout, ErrorService){
 	return {
 		scope: {
 		},
@@ -49,8 +49,8 @@ angular.module('reportApp')
 					res.data.forEach(a => scope.algorithms.push(a));
 					scope.algorithms.forEach(a => { scope.radios[a] = ''; });
 				})
-				.catch(error => {
-					console.error(error);
+				.catch(e => {
+					ErrorService.logError(e, `Could not publish the report.`);
 				})
 				;
 			};
diff --git a/beat/web/reports/static/reports/app/directives/save.js b/beat/web/reports/static/reports/app/directives/save.js
index 0a2a40c53..a2f5b9605 100644
--- a/beat/web/reports/static/reports/app/directives/save.js
+++ b/beat/web/reports/static/reports/app/directives/save.js
@@ -26,7 +26,7 @@
  * 	saves the current report
  */
 angular.module('reportApp')
-.directive("reportSave", ['GroupsService', 'ReportService', 'reportFactory', function(GroupsService, ReportService, reportFactory){
+.directive("reportSave", ['GroupsService', 'ReportService', 'reportFactory', 'ErrorService', function(GroupsService, ReportService, reportFactory, ErrorService){
 	return {
 		scope: {
 		},
@@ -42,6 +42,9 @@ angular.module('reportApp')
 				};
 
 				return reportFactory.updateReport(ReportService.author, ReportService.name, saveData, '')
+				.catch(e => {
+					ErrorService.logError(e, `Could not save the report.`);
+				});
 			};
 
 			el.bind('click', saveReport);
diff --git a/beat/web/reports/static/reports/app/services/errorService.js b/beat/web/reports/static/reports/app/services/errorService.js
new file mode 100644
index 000000000..3343054bb
--- /dev/null
+++ b/beat/web/reports/static/reports/app/services/errorService.js
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ *
+ * This file is part of the beat.web module of the BEAT platform.
+ *
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ *
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+ */
+
+/*
+ * ErrorService
+ * Desc:
+ * 	Centralizes user-facing error-handling in the reports app.
+ * 	Other parts of the app can register errors (i.e. 404s, server errors,
+ * 	invalid input, etc.) with the ErrorService.
+ * 	The ErrorService will (one at a time & synchronously!) pop up an error
+ * 	modal (directives/error.js, "report-error") to let the user know.
+ */
+angular.module('reportApp').factory('ErrorService', [function(){
+	const es = {
+		_errors: [],
+		_hasError: false
+	};
+
+	class ReportError {
+		constructor (errorObj, message) {
+			this._error = errorObj;
+			this._message = message || '';
+		}
+
+		get error () {
+			return this._error;
+		}
+
+		get message () {
+			return this._message;
+		}
+	}
+
+	es.logError = (error, message) => {
+		const newErr = new ReportError(error, message);
+
+		es._errors.push(newErr);
+		_hasError = true;
+	};
+
+	es._getError = () => {
+		const err = es._errors.shift();
+
+		if(es._errors.length === 0){
+			es._hasError = false;
+		}
+
+		return err;
+	}
+
+	return es;
+}]);
diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html
index 408849d16..80aa697e3 100644
--- a/beat/web/reports/templates/reports/report.html
+++ b/beat/web/reports/templates/reports/report.html
@@ -90,6 +90,7 @@
     <script src="{% fingerprint "reports/app/services/urlService.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/services/reportService.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/services/plotService.js" %}" type="text/javascript" charset="utf-8"></script>
+    <script src="{% fingerprint "reports/app/services/errorService.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <!-- directives -->
 
@@ -100,6 +101,7 @@
     <script src="{% fingerprint "reports/app/directives/publish.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/lock.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/save.js" %}" type="text/javascript" charset="utf-8"></script>
+    <script src="{% fingerprint "reports/app/directives/error.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <!-- edit view -->
     {% if not report_number and report.get_status_display == 'Editable' and owner %}
-- 
GitLab