From a561d6ad20429dad71de746ae3361819a1b0370b Mon Sep 17 00:00:00 2001 From: Oliver Foster Date: Wed, 5 Aug 2020 15:41:41 +0100 Subject: [PATCH 1/2] issue/1333 Added error abstraction --- example.json | 16 ++++++++ js/scorm/error.js | 37 +++++++++++++++++ js/scorm/wrapper.js | 98 ++++++++++++++++++++++++++++++--------------- 3 files changed, 119 insertions(+), 32 deletions(-) create mode 100644 js/scorm/error.js diff --git a/example.json b/example.json index 973e7d81..c70c477d 100644 --- a/example.json +++ b/example.json @@ -27,3 +27,19 @@ "_exitStateIfComplete": "auto" } } + + // to be added to course/en/course.json + "_spoor": { + "_errorMessages": { + "title": "WARNING", + "CLIENT_COULD_NOT_CONNECT": "Course could not connect to the LMS", + "SERVER_STATUS_UNSUPPORTED": "ScormWrapper::getStatus: invalid lesson status '{{status}}' received from LMS", + "CLIENT_STATUS_UNSUPPORTED": "ScormWrapper::setStatus: the status '{{status}}' is not supported.", + "CLIENT_COULD_NOT_COMMIT": "Course could not commit data to the LMS\nError {{code}}: {{info}}\nLMS Error Info: {{diagnosticInfo}}", + "CLIENT_NOT_CONNECTED": "Course is not connected to the LMS", + "CLIENT_COULD_NOT_FINISH": "Course could not finish", + "CLIENT_COULD_NOT_GET_PROPERTY": "Course could not get {{property}}\nError Info: {{info}}\nLMS Error Info: {{diagnosticInfo}}", + "CLIENT_COULD_NOT_SET_PROPERTY": "Course could not set {{property}} to {{value}}\nError Info: {{info}}\nLMS Error Info: {{diagnosticInfo}}", + "CLIENT_INVALID_CHOICE_VALUE": "Numeric choice/matching response elements must use a value from 0 to 35 in SCORM 1.2: {{value}}" + } + } diff --git a/js/scorm/error.js b/js/scorm/error.js new file mode 100644 index 00000000..b433cc64 --- /dev/null +++ b/js/scorm/error.js @@ -0,0 +1,37 @@ +define(function() { + + class ScormError { + + constructor(name, data = {}) { + this.name = name; + this.data = data; + } + + } + + ScormError.CLIENT_COULD_NOT_CONNECT = 'CLIENT_COULD_NOT_CONNECT'; + ScormError.SERVER_STATUS_UNSUPPORTED = 'SERVER_STATUS_UNSUPPORTED'; // status + ScormError.CLIENT_STATUS_UNSUPPORTED = 'CLIENT_STATUS_UNSUPPORTED'; // status + ScormError.CLIENT_COULD_NOT_COMMIT = 'CLIENT_COULD_NOT_COMMIT'; // code, info, diagnosticInfo + ScormError.CLIENT_NOT_CONNECTED = 'CLIENT_NOT_CONNECTED'; + ScormError.CLIENT_COULD_NOT_FINISH = 'CLIENT_COULD_NOT_FINISH'; + ScormError.CLIENT_COULD_NOT_GET_PROPERTY = 'CLIENT_COULD_NOT_GET_PROPERTY'; // property, info, diagnosticInfo + ScormError.CLIENT_COULD_NOT_SET_PROPERTY = 'CLIENT_COULD_NOT_SET_PROPERTY'; // property, value, info, diagnosticInfo + ScormError.CLIENT_INVALID_CHOICE_VALUE = 'CLIENT_INVALID_CHOICE_VALUE'; // value + + ScormError.defaultMessages = { + title: 'WARNING', + CLIENT_COULD_NOT_CONNECT: 'Course could not connect to the LMS', + SERVER_STATUS_UNSUPPORTED: `ScormWrapper::getStatus: invalid lesson status '{{status}}' received from LMS`, + CLIENT_STATUS_UNSUPPORTED: `ScormWrapper::setStatus: the status '{{status}}' is not supported.`, + CLIENT_COULD_NOT_COMMIT: 'Course could not commit data to the LMS\nError {{code}}: {{info}}\nLMS Error Info: {{diagnosticInfo}}', + CLIENT_NOT_CONNECTED: 'Course is not connected to the LMS', + CLIENT_COULD_NOT_FINISH: 'Course could not finish', + CLIENT_COULD_NOT_GET_PROPERTY: 'Course could not get {{property}}\nError Info: {{info}}\nLMS Error Info: {{diagnosticInfo}}', + CLIENT_COULD_NOT_SET_PROPERTY: 'Course could not set {{property}} to {{value}}\nError Info: {{info}}\nLMS Error Info: {{diagnosticInfo}}', + CLIENT_INVALID_CHOICE_VALUE: 'Numeric choice/matching response elements must use a value from 0 to 35 in SCORM 1.2: {{value}}' + }; + + return ScormError; + +}); diff --git a/js/scorm/wrapper.js b/js/scorm/wrapper.js index 85ab6e21..f4211fad 100644 --- a/js/scorm/wrapper.js +++ b/js/scorm/wrapper.js @@ -1,7 +1,21 @@ define([ + 'core/js/adapt', 'libraries/SCORM_API_wrapper', - './logger' -], function(pipwerks, Logger) { + './logger', + './error' +], function(Adapt, pipwerks, Logger, ScormError) { + + const { + CLIENT_COULD_NOT_CONNECT, + SERVER_STATUS_UNSUPPORTED, + CLIENT_STATUS_UNSUPPORTED, + CLIENT_COULD_NOT_COMMIT, + CLIENT_NOT_CONNECTED, + CLIENT_COULD_NOT_FINISH, + CLIENT_COULD_NOT_GET_PROPERTY, + CLIENT_COULD_NOT_SET_PROPERTY, + CLIENT_INVALID_CHOICE_VALUE + } = ScormError; /** * IMPORTANT: This wrapper uses the Pipwerks SCORM wrapper and should therefore support both SCORM 1.2 and 2004. Ensure any changes support both versions. @@ -104,7 +118,7 @@ define([ this.initTimedCommit(); } else { - this.handleError('Course could not connect to the LMS'); + this.handleError(new ScormError(CLIENT_COULD_NOT_CONNECT)); } return this.lmsConnected; @@ -168,7 +182,7 @@ define([ case 'unknown': // the SCORM 2004 version of not attempted return status; default: - this.handleError(`ScormWrapper::getStatus: invalid lesson status '${status}' received from LMS`); + this.handleError(new ScormError(SERVER_STATUS_UNSUPPORTED, { status })); return null; } } @@ -188,7 +202,7 @@ define([ this.setFailed(); break; default: - this.handleError(`ScormWrapper::setStatus: the status '${status}' is not supported.`); + this.handleError(new ScormError(CLIENT_STATUS_UNSUPPORTED, { status })); } } @@ -251,7 +265,7 @@ define([ this.logger.debug('ScormWrapper::commit'); if (!this.lmsConnected) { - this.handleError('Course is not connected to the LMS'); + this.handleError(new ScormError(ScormError.CLIENT_COULD_NOT_CONNECT)); return; } @@ -266,13 +280,11 @@ define([ this.commitRetries++; this.initRetryCommit(); } else { - const _errorCode = this.scorm.debug.getCode(); - - let _errorMsg = 'Course could not commit data to the LMS'; - _errorMsg += `\nError ${_errorCode}: ${this.scorm.debug.getInfo(_errorCode)}`; - _errorMsg += `\nLMS Error Info: ${this.scorm.debug.getDiagnosticInfo(_errorCode)}`; - - this.handleError(_errorMsg); + this.handleError(new ScormError(CLIENT_COULD_NOT_COMMIT, { + code: this.scorm.debug.getCode(), + info: this.scorm.debug.getInfo(_errorCode), + diagnosticInfo: this.scorm.debug.getDiagnosticInfo(_errorCode) + })); } } } @@ -282,7 +294,7 @@ define([ this.logger.debug('ScormWrapper::finish'); if (!this.lmsConnected || this.finishCalled) { - this.handleError('Course is not connected to the LMS'); + this.handleError(new ScormError(CLIENT_NOT_CONNECTED)); return; } @@ -315,7 +327,7 @@ define([ this.lmsConnected = false; if (!this.scorm.quit()) { - this.handleError('Course could not finish'); + this.handleError(new ScormError(CLIENT_COULD_NOT_FINISH)); } } @@ -358,7 +370,7 @@ define([ } if (!this.lmsConnected) { - this.handleError('Course is not connected to the LMS'); + this.handleError(new ScormError(CLIENT_NOT_CONNECTED)); return; } @@ -370,11 +382,11 @@ define([ if (_errorCode === 403) { this.logger.warn('ScormWrapper::getValue: data model element not initialized'); } else { - _errorMsg += `Course could not get ${_property}`; - _errorMsg += `\nError Info: ${this.scorm.debug.getInfo(_errorCode)}`; - _errorMsg += `\nLMS Error Info: ${this.scorm.debug.getDiagnosticInfo(_errorCode)}`; - - this.handleError(_errorMsg); + this.handleError(new ScormError(CLIENT_COULD_NOT_GET_PROPERTY, { + property: _property, + info: this.scorm.debug.getInfo(_errorCode), + diagnosticInfo: this.scorm.debug.getDiagnosticInfo(_errorCode) + })); } } this.logger.debug(`ScormWrapper::getValue: returning ${_value}`); @@ -390,7 +402,7 @@ define([ } if (!this.lmsConnected) { - this.handleError('Course is not connected to the LMS'); + this.handleError(new ScormError(CLIENT_NOT_CONNECTED)); return; } @@ -404,11 +416,12 @@ define([ * So, we should throw an error _only_ if there was a valid error code... */ if (_errorCode !== 0) { - _errorMsg += `Course could not set ${_property} to ${_value}`; - _errorMsg += `\nError Info: ${this.scorm.debug.getInfo(_errorCode)}`; - _errorMsg += `\nLMS Error Info: ${this.scorm.debug.getDiagnosticInfo(_errorCode)}`; - - this.handleError(_errorMsg); + this.handleError(new ScormError(CLIENT_COULD_NOT_SET_PROPERTY, { + property: _property, + value: _value, + info: this.scorm.debug.getInfo(_errorCode), + diagnosticInfo: this.scorm.debug.getDiagnosticInfo(_errorCode) + })); } else { this.logger.warn(`ScormWrapper::setValue: LMS reported that the 'set' call failed but then said there was no error!`); } @@ -431,7 +444,7 @@ define([ } if (!this.lmsConnected) { - this.handleError('Course is not connected to the LMS'); + this.handleError(new ScormError(CLIENT_NOT_CONNECTED)); return false; } @@ -465,12 +478,33 @@ define([ this.commit(); } - handleError(_msg) { - this.logger.error(_msg); + handleError(error) { + + if (!Adapt.get('_isStarted')) { + Adapt.once('contentObjectView:ready', this.handleError.bind(this, error)); + return; + } + + const config = Adapt.course.get('_spoor'); + const errorMessages = Object.assign({}, ScormError.defaultMessages, config && config._errorMessages); + const message = Handlebars.compile(errorMessages[error.name])(error.data); + + switch (error.name) { + case CLIENT_COULD_NOT_CONNECT: + Adapt.notify.popup({ + _isCancellable: false, + title: errorMessages['title'], + body: message + }); + return; + } + + this.logger.error(message); - if (!this.suppressErrors && (!this.logOutputWin || this.logOutputWin.closed) && confirm(`An error has occured:\n\n${_msg}\n\nPress 'OK' to view debug information to send to technical support.`)) { + if (!this.suppressErrors && (!this.logOutputWin || this.logOutputWin.closed) && confirm(`An error has occured:\n\n${message}\n\nPress 'OK' to view debug information to send to technical support.`)) { this.showDebugWindow(); } + } getInteractionCount() { @@ -702,7 +736,7 @@ define([ i = parseInt(r); if (isNaN(i) || i < 10 || i > 35) { - self.handleError('Numeric choice/matching response elements must use a value from 0 to 35 in SCORM 1.2'); + self.handleError(new ScormError(CLIENT_INVALID_CHOICE_VALUE, { value })); } return Number(i).toString(36); // 10 maps to 'a', 11 maps to 'b', ..., 35 maps to 'z' From c6a3262fff7bead4e26645bdbb9e556008d80aa9 Mon Sep 17 00:00:00 2001 From: Oliver Foster Date: Wed, 5 Aug 2020 16:01:57 +0100 Subject: [PATCH 2/2] issue/1333 Moved variables to talk of messages not errors --- example.json | 2 +- js/scorm/wrapper.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example.json b/example.json index c70c477d..d763d26f 100644 --- a/example.json +++ b/example.json @@ -30,7 +30,7 @@ // to be added to course/en/course.json "_spoor": { - "_errorMessages": { + "_messages": { "title": "WARNING", "CLIENT_COULD_NOT_CONNECT": "Course could not connect to the LMS", "SERVER_STATUS_UNSUPPORTED": "ScormWrapper::getStatus: invalid lesson status '{{status}}' received from LMS", diff --git a/js/scorm/wrapper.js b/js/scorm/wrapper.js index f4211fad..4dfee6d6 100644 --- a/js/scorm/wrapper.js +++ b/js/scorm/wrapper.js @@ -486,8 +486,8 @@ define([ } const config = Adapt.course.get('_spoor'); - const errorMessages = Object.assign({}, ScormError.defaultMessages, config && config._errorMessages); - const message = Handlebars.compile(errorMessages[error.name])(error.data); + const messages = Object.assign({}, ScormError.defaultMessages, config && config._messages); + const message = Handlebars.compile(messages[error.name])(error.data); switch (error.name) { case CLIENT_COULD_NOT_CONNECT: