Improved error handling when upgrading content.

d6
Frode Petterson 2014-09-29 17:13:47 +02:00
parent a38a0f1253
commit 2011e856c0
1 changed files with 86 additions and 75 deletions

View File

@ -2,18 +2,18 @@ var H5PUpgrades = H5PUpgrades || {};
(function ($) { (function ($) {
var info, $container, librariesCache = {}; var info, $container, librariesCache = {};
// Initialize // Initialize
$(document).ready(function () { $(document).ready(function () {
// Get library info // Get library info
info = H5PIntegration.getLibraryInfo(); info = H5PIntegration.getLibraryInfo();
// Get and reset container // Get and reset container
$container = $('#h5p-admin-container').html('<p>' + info.message + '</p>'); $container = $('#h5p-admin-container').html('<p>' + info.message + '</p>');
// Make it possible to select version // Make it possible to select version
var $version = $(getVersionSelect(info.versions)).appendTo($container); var $version = $(getVersionSelect(info.versions)).appendTo($container);
// Add "go" button // Add "go" button
$('<button/>', { $('<button/>', {
class: 'h5p-admin-upgrade-button', class: 'h5p-admin-upgrade-button',
@ -23,11 +23,11 @@ var H5PUpgrades = H5PUpgrades || {};
new ContentUpgrade($version.val()); new ContentUpgrade($version.val());
} }
}).appendTo($container); }).appendTo($container);
}); });
/** /**
* Generate html for version select. * Generate html for version select.
* *
* @param {Object} versions * @param {Object} versions
* @returns {String} * @returns {String}
*/ */
@ -41,18 +41,18 @@ var H5PUpgrades = H5PUpgrades || {};
return html; return html;
} }
}; };
/** /**
* Private. Helps process each property on the given object asynchronously in serial order. * Private. Helps process each property on the given object asynchronously in serial order.
* *
* @param {Object} obj * @param {Object} obj
* @param {Function} process * @param {Function} process
* @param {Function} finished * @param {Function} finished
*/ */
var asyncSerial = function (obj, process, finished) { var asyncSerial = function (obj, process, finished) {
var id, isArray = obj instanceof Array; var id, isArray = obj instanceof Array;
// Keep track of each property that belongs to this object. // Keep track of each property that belongs to this object.
if (!isArray) { if (!isArray) {
var ids = []; var ids = [];
for (id in obj) { for (id in obj) {
@ -61,9 +61,9 @@ var H5PUpgrades = H5PUpgrades || {};
} }
} }
} }
var i = -1; // Keeps track of the current property var i = -1; // Keeps track of the current property
/** /**
* Private. Process the next property * Private. Process the next property
*/ */
@ -71,10 +71,10 @@ var H5PUpgrades = H5PUpgrades || {};
id = isArray ? i : ids[i]; id = isArray ? i : ids[i];
process(id, obj[id], check); process(id, obj[id], check);
}; };
/** /**
* Private. Check if we're done or have an error. * Private. Check if we're done or have an error.
* *
* @param {String} err * @param {String} err
*/ */
var check = function (err) { var check = function (err) {
@ -89,13 +89,13 @@ var H5PUpgrades = H5PUpgrades || {};
} }
}, 0); }, 0);
}; };
check(); // Start check(); // Start
}; };
/** /**
* Make it easy to keep track of version details. * Make it easy to keep track of version details.
* *
* @param {String} version * @param {String} version
* @param {Number} libraryId * @param {Number} libraryId
* @returns {_L1.Version} * @returns {_L1.Version}
@ -103,19 +103,19 @@ var H5PUpgrades = H5PUpgrades || {};
function Version(version, libraryId) { function Version(version, libraryId) {
if (libraryId !== undefined) { if (libraryId !== undefined) {
version = info.versions[libraryId]; version = info.versions[libraryId];
// Public // Public
this.libraryId = libraryId; this.libraryId = libraryId;
} }
var versionSplit = version.split('.', 3); var versionSplit = version.split('.', 3);
// Public // Public
this.major = versionSplit[0]; this.major = versionSplit[0];
this.minor = versionSplit[1]; this.minor = versionSplit[1];
/** /**
* Public. Custom string for this object. * Public. Custom string for this object.
* *
* @returns {String} * @returns {String}
*/ */
this.toString = function () { this.toString = function () {
@ -125,17 +125,17 @@ var H5PUpgrades = H5PUpgrades || {};
/** /**
* Displays a throbber in the status field. * Displays a throbber in the status field.
* *
* @param {String} msg * @param {String} msg
* @returns {_L1.Throbber} * @returns {_L1.Throbber}
*/ */
function Throbber(msg) { function Throbber(msg) {
var $throbber = H5PUtils.throbber(msg); var $throbber = H5PUtils.throbber(msg);
$container.html('').append($throbber); $container.html('').append($throbber);
/** /**
* Makes it possible to set the progress. * Makes it possible to set the progress.
* *
* @param {String} progress * @param {String} progress
*/ */
this.setProgress = function (progress) { this.setProgress = function (progress) {
@ -145,16 +145,16 @@ var H5PUpgrades = H5PUpgrades || {};
/** /**
* Start a new content upgrade. * Start a new content upgrade.
* *
* @param {Number} libraryId * @param {Number} libraryId
* @returns {_L1.ContentUpgrade} * @returns {_L1.ContentUpgrade}
*/ */
function ContentUpgrade(libraryId) { function ContentUpgrade(libraryId) {
var self = this; var self = this;
// Get selected version // Get selected version
self.version = new Version(null, libraryId); self.version = new Version(null, libraryId);
// Create throbber with loading text and progress // Create throbber with loading text and progress
self.throbber = new Throbber(info.inProgress.replace('%ver', self.version)); self.throbber = new Throbber(info.inProgress.replace('%ver', self.version));
@ -164,36 +164,36 @@ var H5PUpgrades = H5PUpgrades || {};
token: info.token token: info.token
}); });
} }
/** /**
* Get the next batch and start processing it. * Get the next batch and start processing it.
* *
* @param {Object} outData * @param {Object} outData
*/ */
ContentUpgrade.prototype.nextBatch = function (outData) { ContentUpgrade.prototype.nextBatch = function (outData) {
var self = this; var self = this;
$.post(info.infoUrl, outData, function (inData) { $.post(info.infoUrl, outData, function (inData) {
if (!(inData instanceof Object)) { if (!(inData instanceof Object)) {
// Print errors from backend // Print errors from backend
return self.setStatus(inData); return self.setStatus(inData);
} }
if (inData.left === 0) { if (inData.left === 0) {
// Nothing left to process // Nothing left to process
return self.setStatus(info.done); return self.setStatus(info.done);
} }
self.left = inData.left; self.left = inData.left;
self.token = inData.token; self.token = inData.token;
// Start processing // Start processing
self.processBatch(inData.params); self.processBatch(inData.params);
}); });
}; };
/** /**
* Set current status message. * Set current status message.
* *
* @param {String} msg * @param {String} msg
*/ */
ContentUpgrade.prototype.setStatus = function (msg) { ContentUpgrade.prototype.setStatus = function (msg) {
@ -202,28 +202,37 @@ var H5PUpgrades = H5PUpgrades || {};
/** /**
* Process the given parameters. * Process the given parameters.
* *
* @param {Object} parameters * @param {Object} parameters
*/ */
ContentUpgrade.prototype.processBatch = function (parameters) { ContentUpgrade.prototype.processBatch = function (parameters) {
var self = this; var self = this;
var upgraded = {}; // Track upgraded params var upgraded = {}; // Track upgraded params
var current = 0; // Track progress var current = 0; // Track progress
asyncSerial(parameters, function (id, params, next) { asyncSerial(parameters, function (id, params, next) {
try {
// Make params possible to work with // Make params possible to work with
params = JSON.parse(params); params = JSON.parse(params);
if (!(params instanceof Object)) {
throw true;
}
}
catch (event) {
return next(info.errorContent.replace('%id', id) + ' ' + info.errorParamsBroken);
}
// Upgrade this content. // Upgrade this content.
self.upgrade(info.library.name, new Version(info.library.version), self.version, params, function (err, params) { self.upgrade(info.library.name, new Version(info.library.version), self.version, params, function (err, params) {
if (!err) { if (err) {
upgraded[id] = JSON.stringify(params); return next(info.errorContent.replace('%id', id) + ' ' + err);
current++;
self.throbber.setProgress(Math.round((info.total - self.left + current) / (info.total / 100)) + ' %');
} }
next(err);
upgraded[id] = JSON.stringify(params);
current++;
self.throbber.setProgress(Math.round((info.total - self.left + current) / (info.total / 100)) + ' %');
next();
}); });
}, function (err) { }, function (err) {
@ -240,10 +249,10 @@ var H5PUpgrades = H5PUpgrades || {};
}); });
}); });
}; };
/** /**
* Upgade the given content. * Upgade the given content.
* *
* @param {String} name * @param {String} name
* @param {Version} oldVersion * @param {Version} oldVersion
* @param {Version} newVersion * @param {Version} newVersion
@ -253,19 +262,19 @@ var H5PUpgrades = H5PUpgrades || {};
*/ */
ContentUpgrade.prototype.upgrade = function (name, oldVersion, newVersion, params, next) { ContentUpgrade.prototype.upgrade = function (name, oldVersion, newVersion, params, next) {
var self = this; var self = this;
// Load library details and upgrade routines // Load library details and upgrade routines
self.loadLibrary(name, newVersion, function (err, library) { self.loadLibrary(name, newVersion, function (err, library) {
if (err) { if (err) {
return next(err); return next(err);
} }
// Run upgrade routines on params // Run upgrade routines on params
self.processParams(library, oldVersion, newVersion, params, function (err, params) { self.processParams(library, oldVersion, newVersion, params, function (err, params) {
if (err) { if (err) {
return next(err); return next(err);
} }
// Check if any of the sub-libraries need upgrading // Check if any of the sub-libraries need upgrading
asyncSerial(library.semantics, function (index, field, next) { asyncSerial(library.semantics, function (index, field, next) {
self.processField(field, params[field.name], function (err, upgradedParams) { self.processField(field, params[field.name], function (err, upgradedParams) {
@ -280,24 +289,24 @@ var H5PUpgrades = H5PUpgrades || {};
}); });
}); });
}; };
/** /**
* Load library data needed for content upgrade. * Load library data needed for content upgrade.
* *
* @param {String} name * @param {String} name
* @param {Version} version * @param {Version} version
* @param {Function} next * @param {Function} next
*/ */
ContentUpgrade.prototype.loadLibrary = function (name, version, next) { ContentUpgrade.prototype.loadLibrary = function (name, version, next) {
var self = this; var self = this;
var key = name + '/' + version.major + '/' + version.minor; var key = name + '/' + version.major + '/' + version.minor;
if (librariesCache[key] !== undefined) { if (librariesCache[key] !== undefined) {
// Library has been loaded before. Return cache. // Library has been loaded before. Return cache.
next(null, librariesCache[key]); next(null, librariesCache[key]);
return; return;
} }
$.ajax({ $.ajax({
dataType: 'json', dataType: 'json',
cache: true, cache: true,
@ -306,7 +315,7 @@ var H5PUpgrades = H5PUpgrades || {};
next(info.errorData.replace('%lib', name + ' ' + version)); next(info.errorData.replace('%lib', name + ' ' + version));
}).done(function (library) { }).done(function (library) {
librariesCache[key] = library; librariesCache[key] = library;
if (library.upgradesScript) { if (library.upgradesScript) {
self.loadScript(library.upgradesScript, function (err) { self.loadScript(library.upgradesScript, function (err) {
if (err) { if (err) {
@ -320,10 +329,10 @@ var H5PUpgrades = H5PUpgrades || {};
} }
}); });
}; };
/** /**
* Load script with upgrade hooks. * Load script with upgrade hooks.
* *
* @param {String} url * @param {String} url
* @param {Function} next * @param {Function} next
*/ */
@ -338,10 +347,10 @@ var H5PUpgrades = H5PUpgrades || {};
next(); next();
}); });
}; };
/** /**
* Run upgrade hooks on params. * Run upgrade hooks on params.
* *
* @param {Object} library * @param {Object} library
* @param {Version} oldVersion * @param {Version} oldVersion
* @param {Version} newVersion * @param {Version} newVersion
@ -354,7 +363,7 @@ var H5PUpgrades = H5PUpgrades || {};
// Upgrades script should be loaded so the upgrades should be here. // Upgrades script should be loaded so the upgrades should be here.
return next(info.errorScript.replace('%lib', library.name + ' ' + newVersion)); return next(info.errorScript.replace('%lib', library.name + ' ' + newVersion));
} }
// No upgrades script. Move on // No upgrades script. Move on
return next(null, params); return next(null, params);
} }
@ -362,7 +371,7 @@ var H5PUpgrades = H5PUpgrades || {};
// Run upgrade hooks. Start by going through major versions // Run upgrade hooks. Start by going through major versions
asyncSerial(H5PUpgrades[library.name], function (major, minors, nextMajor) { asyncSerial(H5PUpgrades[library.name], function (major, minors, nextMajor) {
if (major < oldVersion.major || major > newVersion.major) { if (major < oldVersion.major || major > newVersion.major) {
// Older than the current version or newer than the selected // Older than the current version or newer than the selected
nextMajor(); nextMajor();
} }
else { else {
@ -374,14 +383,16 @@ var H5PUpgrades = H5PUpgrades || {};
} }
else { else {
// We found an upgrade hook, run it // We found an upgrade hook, run it
if (upgrade.contentUpgrade !== undefined && typeof upgrade.contentUpgrade === 'function') { var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade);
upgrade.contentUpgrade(params, function (err, upgradedParams) {
try {
unnecessaryWrapper(params, function (err, upgradedParams) {
params = upgradedParams; params = upgradedParams;
nextMinor(err); nextMinor(err);
}); });
} }
else { catch (err) {
nextMinor(info.errorScript.replace('%lib', library.name + ' ' + newVersion)); next(err);
} }
} }
}, nextMajor); }, nextMajor);
@ -390,27 +401,27 @@ var H5PUpgrades = H5PUpgrades || {};
next(err, params); next(err, params);
}); });
}; };
/** /**
* Process parameter fields to find and upgrade sub-libraries. * Process parameter fields to find and upgrade sub-libraries.
* *
* @param {Object} field * @param {Object} field
* @param {Object} params * @param {Object} params
* @param {Function} next * @param {Function} next
*/ */
ContentUpgrade.prototype.processField = function (field, params, next) { ContentUpgrade.prototype.processField = function (field, params, next) {
var self = this; var self = this;
if (params === undefined) { if (params === undefined) {
return next(); return next();
} }
switch (field.type) { switch (field.type) {
case 'library': case 'library':
if (params.library === undefined || params.params === undefined) { if (params.library === undefined || params.params === undefined) {
return next(); return next();
} }
// Look for available upgrades // Look for available upgrades
var usedLib = params.library.split(' ', 2); var usedLib = params.library.split(' ', 2);
for (var i = 0; i < field.options.length; i++) { for (var i = 0; i < field.options.length; i++) {
@ -419,14 +430,14 @@ var H5PUpgrades = H5PUpgrades || {};
if (availableLib[1] === usedLib[1]) { if (availableLib[1] === usedLib[1]) {
return next(); // Same version return next(); // Same version
} }
// We have different versions // We have different versions
var usedVer = new Version(usedLib[1]); var usedVer = new Version(usedLib[1]);
var availableVer = new Version(availableLib[1]); var availableVer = new Version(availableLib[1]);
if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) { if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) {
return next(); // Larger or same version that's available return next(); // Larger or same version that's available
} }
// A newer version is available, upgrade params // A newer version is available, upgrade params
return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgraded) { return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgraded) {
if (!err) { if (!err) {
@ -484,4 +495,4 @@ var H5PUpgrades = H5PUpgrades || {};
} }
}; };
})(H5P.jQuery); })(H5P.jQuery);