@openveo/api
Advanced tools
Comparing version 2.0.0 to 3.0.0
@@ -0,1 +1,12 @@ | ||
# 3.0.0 / 2016-05-30 | ||
- Update logger | ||
- Update Models to define Entity and ContentEntity | ||
- Update Plugin interface | ||
- Add Taxonomy Model in API | ||
- Add Controller interface | ||
- Add search index | ||
- Add increase function in database interface | ||
- Add migration script | ||
- Add Helper to validate JSON Object | ||
# 2.0.0 / 2016-02-19 | ||
@@ -2,0 +13,0 @@ |
'use strict'; | ||
// Module dependencies | ||
var path = require('path'); | ||
@@ -20,2 +19,6 @@ | ||
module.exports.EntityModel = process.requireAPI('lib/models/EntityModel.js'); | ||
module.exports.ContentModel = process.requireAPI('lib/models/ContentModel.js'); | ||
module.exports.EntityProvider = process.requireAPI('lib/providers/EntityProvider.js'); | ||
module.exports.middlewares = process.requireAPI('lib/middlewares/index.js'); | ||
module.exports.errors = process.requireAPI('lib/errors/index.js'); | ||
module.exports.controllers = process.requireAPI('lib/controllers/index.js'); |
@@ -21,2 +21,4 @@ 'use strict'; | ||
var permissions; | ||
var superAdminId; | ||
var anonymousUserId; | ||
@@ -41,2 +43,3 @@ /** | ||
* | ||
* @method setPlugins | ||
* @param {Array} subPlugins The list of plugins | ||
@@ -66,2 +69,3 @@ */ | ||
* | ||
* @method setMenu | ||
* @param {Array} newMenu The list of back office menu links | ||
@@ -91,2 +95,3 @@ */ | ||
* | ||
* @method setDatabase | ||
* @param {Database} newDatabase The new database of the application | ||
@@ -101,2 +106,3 @@ */ | ||
* | ||
* @method setWebServiceScopes | ||
* @param {Object} newScopes The new list of scopes of the web service | ||
@@ -126,2 +132,3 @@ */ | ||
* | ||
* @method setPermissions | ||
* @param {Object} permissions The new list of permissions | ||
@@ -151,2 +158,3 @@ */ | ||
* | ||
* @method setEntities | ||
* @param {Object} newEntities The list of entities | ||
@@ -172,1 +180,63 @@ */ | ||
}; | ||
/** | ||
* Gets the id of the super administrator. | ||
* | ||
* @example | ||
* | ||
* var api = require('@openveo/api'); | ||
* api.applicationStorage.getSuperAdminId(); | ||
* | ||
* @method getSuperAdminId | ||
* @return {String} The super administrator id | ||
*/ | ||
module.exports.getSuperAdminId = function() { | ||
return superAdminId; | ||
}; | ||
/** | ||
* Sets the id of the super administrator. | ||
* | ||
* It can be set only once. | ||
* | ||
* @method setSuperAdminId | ||
* @param {String} id The id of the super administrator | ||
* @throws {Error} An error if super administrator id is already set | ||
*/ | ||
module.exports.setSuperAdminId = function(id) { | ||
if (superAdminId === undefined) | ||
superAdminId = id; | ||
else | ||
throw new Error('Super administrator id can only be set once'); | ||
}; | ||
/** | ||
* Gets the id of the anonymous user. | ||
* | ||
* @example | ||
* | ||
* var api = require('@openveo/api'); | ||
* api.applicationStorage.getAnonymousUserId(); | ||
* | ||
* @method getAnonymousUserId | ||
* @return {String} The super administrator id | ||
*/ | ||
module.exports.getAnonymousUserId = function() { | ||
return anonymousUserId; | ||
}; | ||
/** | ||
* Sets the id of the anonymous user. | ||
* | ||
* It can be set only once. | ||
* | ||
* @method setAnonymousUserId | ||
* @param {String} id The id of the anonymous user | ||
* @throws {Error} An error if anonymous user id is already set | ||
*/ | ||
module.exports.setAnonymousUserId = function(id) { | ||
if (anonymousUserId === undefined) | ||
anonymousUserId = id; | ||
else | ||
throw new Error('Anonymous user id can only be set once'); | ||
}; |
@@ -191,1 +191,84 @@ 'use strict'; | ||
}; | ||
/** | ||
* Gets the list of indexes for a collection. | ||
* | ||
* @method getIndexes | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Array** The list of indexes | ||
*/ | ||
Database.prototype.getIndexes = function() { | ||
throw new Error('getIndexes method not implemented for this database'); | ||
}; | ||
/** | ||
* Creates indexes for a collection. | ||
* | ||
* @method createIndexes | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Array} indexes A list of indexes using MongoDB format | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Object** Information about the operation | ||
*/ | ||
Database.prototype.createIndexes = function() { | ||
throw new Error('createIndexes method not implemented for this database'); | ||
}; | ||
/** | ||
* Gets an express-session store for the database. | ||
* | ||
* @method getStore | ||
* @param {String} collection The collection to work on | ||
* @return {Store} An express-session store | ||
*/ | ||
Database.prototype.getStore = function() { | ||
throw new Error('getStore method not implemented for this database'); | ||
}; | ||
/** | ||
* increase values in several documents from collection. | ||
* | ||
* @method increase | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Object} filter Filters formatted like MongoDB filters | ||
* @param {Object} data Document data | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Number** The number of increased documents | ||
*/ | ||
Database.prototype.increase = function() { | ||
throw new Error('increase method not implemented for this database'); | ||
}; | ||
/** | ||
* Renames a collection. | ||
* | ||
* @method renameCollection | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {String} target The new name of the collection | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
Database.prototype.renameCollection = function() { | ||
throw new Error('renameCollection method not implemented for this database'); | ||
}; | ||
/** | ||
* Remove a collection from the database | ||
* | ||
* @method removeCollection | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
Database.prototype.removeCollection = function() { | ||
throw new Error('removeCollection method not implemented for this database'); | ||
}; |
@@ -9,2 +9,4 @@ 'use strict'; | ||
var mongodb = require('mongodb'); | ||
var session = require('express-session'); | ||
var MongoStore = require('connect-mongo')(session); | ||
var Database = process.requireAPI('lib/Database.js'); | ||
@@ -275,3 +277,3 @@ var utilExt = process.requireAPI('lib/util.js'); | ||
var paginate = { | ||
count: limit, | ||
limit: limit, | ||
page: page, | ||
@@ -289,1 +291,119 @@ pages: Math.ceil(count / limit), | ||
}; | ||
/** | ||
* Gets the list of indexes for a collection. | ||
* | ||
* @method getIndexes | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Array** The list of indexes | ||
*/ | ||
MongoDatabase.prototype.getIndexes = function(collection, callback) { | ||
this.db.collection(collection, function(error, fetchedCollection) { | ||
if (error) | ||
return callback(error); | ||
fetchedCollection.indexes(callback); | ||
}); | ||
}; | ||
/** | ||
* Creates indexes for a collection. | ||
* | ||
* @method createIndexes | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Array} indexes A list of indexes using MongoDB format | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Object** Information about the operation | ||
*/ | ||
MongoDatabase.prototype.createIndexes = function(collection, indexes, callback) { | ||
this.db.collection(collection, function(error, fetchedCollection) { | ||
if (error) | ||
return callback(error); | ||
fetchedCollection.createIndexes(indexes, callback); | ||
}); | ||
}; | ||
/** | ||
* Gets an express-session store for this database. | ||
* | ||
* @method getStore | ||
* @param {String} collection The collection to work on | ||
* @return {Store} An express-session store | ||
*/ | ||
MongoDatabase.prototype.getStore = function(collection) { | ||
return new MongoStore({db: this.db, collection: collection}); | ||
}; | ||
/** | ||
* increase values in several documents from collection. | ||
* | ||
* @method increase | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Object} filter Filters formatted like MongoDB filters | ||
* @param {Object} data Document data | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Number** The number of increased documents | ||
*/ | ||
MongoDatabase.prototype.increase = function(collection, filter, data, callback) { | ||
var increase = {$inc: data}; | ||
this.db.collection(collection, function(error, fetchedCollection) { | ||
if (error) | ||
return callback(error); | ||
fetchedCollection.updateMany(filter, increase, function(error, result) { | ||
if (error) | ||
callback(error); | ||
else | ||
callback(null, result.modifiedCount); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Renames a collection. | ||
* | ||
* @method renameCollection | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {String} target The new name of the collection | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
MongoDatabase.prototype.renameCollection = function(collection, target, callback) { | ||
var self = this; | ||
self.db.listCollections({name: collection}).toArray(function(error, item) { | ||
if (!item.length) | ||
return callback(); | ||
self.db.collection(collection, function(error, fetchedCollection) { | ||
if (error) | ||
return callback(error); | ||
fetchedCollection.rename(target, callback); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Remove a collection from the database | ||
* | ||
* @method removeCollection | ||
* @async | ||
* @param {String} collection The collection to work on | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
MongoDatabase.prototype.removeCollection = function(collection, callback) { | ||
this.db.dropCollection(collection, callback); | ||
}; |
@@ -179,2 +179,5 @@ 'use strict'; | ||
os.end(); | ||
}); | ||
os.on('finish', function() { | ||
callback(); | ||
@@ -208,3 +211,3 @@ }); | ||
* extract files | ||
* @param {Function} callback The function to call when done | ||
* @param {Function} [callback] The function to call when done | ||
* - **Error** The error if an error occurred, null otherwise | ||
@@ -214,10 +217,12 @@ */ | ||
var extractTimeout; | ||
var onError = function(error) { | ||
if (extractTimeout) | ||
clearTimeout(extractTimeout); | ||
var streamError; | ||
callback(error); | ||
callback = callback || function(error) { | ||
if (error) | ||
process.logger.error('Extract error', {error: error}); | ||
else | ||
process.logger.silly(filePath + ' extracted into ' + destinationPath); | ||
}; | ||
if (filePath && destinationPath && callback) { | ||
if (filePath && destinationPath) { | ||
@@ -231,2 +236,10 @@ // Prepare the extractor with destination path | ||
var onError = function(error) { | ||
if (extractTimeout) | ||
clearTimeout(extractTimeout); | ||
streamError = error; | ||
extractor.end(); | ||
}; | ||
// Handle extraction end | ||
@@ -237,3 +250,3 @@ extractor.on('end', function() { | ||
callback(); | ||
callback(streamError); | ||
}); | ||
@@ -259,3 +272,4 @@ | ||
} | ||
} else | ||
callback(new TypeError('Invalid filePath and / or destinationPath, expected strings')); | ||
}; | ||
@@ -341,4 +355,7 @@ | ||
* - **String** The file content or null if an error occurred | ||
* @throws {TypeError} An error if callback is not speficied | ||
*/ | ||
module.exports.getJSONFileContent = function(filePath, callback) { | ||
if (!filePath) | ||
return callback(new TypeError('Invalid file path, expected a string')); | ||
@@ -358,7 +375,7 @@ // Check if file exists | ||
} else { | ||
var dataAsJson; | ||
try { | ||
// Try to parse file data as JSON content | ||
var dataAsJson = JSON.parse(data); | ||
callback(null, dataAsJson); | ||
dataAsJson = JSON.parse(data); | ||
@@ -368,10 +385,10 @@ } catch (e) { | ||
} | ||
callback(null, dataAsJson); | ||
} | ||
}); | ||
} | ||
else | ||
} else | ||
callback(new Error('Missing file ' + filePath)); | ||
}); | ||
}; | ||
@@ -388,6 +405,16 @@ | ||
* @param {String} directoryPath The directory system path to create | ||
* @param {Function} callback The function to call when done | ||
* @param {Function} [callback] The function to call when done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
module.exports.mkdir = function(directoryPath, callback) { | ||
callback = callback || function(error) { | ||
if (error) | ||
process.logger.error('mkdir error', {error: error}); | ||
else | ||
process.logger.silly(directoryPath + ' directory created'); | ||
}; | ||
if (!directoryPath) | ||
return callback(new TypeError('Invalid directory path, expected a string')); | ||
fs.exists(directoryPath, function(exists) { | ||
@@ -407,6 +434,16 @@ if (exists) | ||
* @param {String} directoryPath Path of the directory to remove | ||
* @param {Function} callback The function to call when done | ||
* @param {Function} [callback] The function to call when done | ||
* - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
module.exports.rmdir = function(directoryPath, callback) { | ||
callback = callback || function(error) { | ||
if (error) | ||
process.logger.error('rmdir error', {error: error}); | ||
else | ||
process.logger.silly(directoryPath + ' directory removed'); | ||
}; | ||
if (!directoryPath) | ||
return callback(new TypeError('Invalid directory path, expected a string')); | ||
fs.exists(directoryPath, function(exists) { | ||
@@ -413,0 +450,0 @@ if (!exists) |
112
lib/i18n.js
@@ -21,8 +21,8 @@ 'use strict'; | ||
* Search is made on i18n directory and all plugin's i18n directories. | ||
* If the same dictionary name is found twice (same file name in | ||
* different i18n directories), dictionaries are merged. | ||
* If the same dictionary name is found twice (same file name in different i18n directories), | ||
* dictionaries are merged. | ||
* | ||
* @example | ||
* var i18n = require('@openveo/api').i18n; | ||
* i18n.getTranslations('login', 'fr-FR', null, function(translations){ | ||
* i18n.getTranslations('login', 'fr-FR', function(error, translations){ | ||
* console.log(translations); | ||
@@ -33,3 +33,3 @@ * }); | ||
* var i18n = require('@openveo/api').i18n; | ||
* i18n.getTranslations("back-office", "en", "admin", function(translations){ | ||
* i18n.getTranslations("back-office", "en", function(error, translations){ | ||
* console.log(translations); | ||
@@ -44,3 +44,4 @@ * }); | ||
* @param {Function} callback Function to call when its done | ||
* - *Object* A JavaScript object containing all translations | ||
* - **Error** An error if something went wrong | ||
* - **Object** A JavaScript object containing all translations | ||
*/ | ||
@@ -51,3 +52,3 @@ module.exports.getTranslations = function(dictionary, code, callback) { | ||
if (!dictionary) | ||
return callback(translations); | ||
return callback(null, translations); | ||
@@ -59,62 +60,69 @@ code = code || 'en'; | ||
var country = countryCode[1]; | ||
var asyncFunctions = []; | ||
var i18nDirectories = [path.normalize(process.root + '/i18n')]; | ||
var pluginsWithi18n = []; | ||
// Get all plugins with a translation directory | ||
plugins.forEach(function(plugin) { | ||
if (plugin.i18nDirectory) { | ||
i18nDirectories.push(plugin.i18nDirectory); | ||
pluginsWithi18n.push(plugin.name); | ||
} | ||
}); | ||
// Read all i18n directories | ||
async.map(i18nDirectories, fs.readdir, function(error, directoriesFiles) { | ||
var i = 0; | ||
// Plugin has an i18n directory | ||
// Read files in the directory to find the expected dictionary | ||
asyncFunctions.push(function(callback) { | ||
fs.readdir(plugin.i18nDirectory, function(error, directoryFiles) { | ||
if (error) { | ||
process.error(error.message, { | ||
plugin: plugin.name, | ||
dir: plugin.i18nDirectory | ||
}); | ||
callback(new Error('An error occured while reading the i18n directory')); | ||
return; | ||
} | ||
var translationFile; | ||
var pluginNameUpperCase = plugin.name.toUpperCase(); | ||
// Iterate through i18n directories | ||
directoriesFiles.forEach(function(directoryFiles) { | ||
var translationFile, | ||
languageFile, | ||
defaultTranslationFile; | ||
// Iterate through directory files to find the dictionary | ||
for (var i = 0; i < directoryFiles.length; i++) { | ||
var fileName = directoryFiles[i]; | ||
if (fileName === dictionary + '-' + language + '_' + country + '.json') { | ||
translationFile = fileName; | ||
break; | ||
} else if (fileName === dictionary + '-' + language + '.json') | ||
translationFile = fileName; | ||
} | ||
// Iterate through directory files | ||
directoryFiles.forEach(function(fileName) { | ||
try { | ||
// File name matches the given name, language and country code | ||
if (fileName === dictionary + '-' + language + '_' + country + '.json') { | ||
translationFile = fileName; | ||
return; | ||
} | ||
if (translationFile) { | ||
var pluginTranslations = require(path.join(plugin.i18nDirectory, translationFile)); | ||
var encapsulatedPluginTranslations; | ||
translations = translations || {}; | ||
// File name matches given name and language | ||
else if (fileName === dictionary + '-' + language + '.json') | ||
languageFile = fileName; | ||
// Make sure translations are contained in an object with the plugin name as the key | ||
if (Object.keys(pluginTranslations).length > 1 || !pluginTranslations[pluginNameUpperCase]) { | ||
encapsulatedPluginTranslations = {}; | ||
encapsulatedPluginTranslations[pluginNameUpperCase] = pluginTranslations; | ||
} else | ||
encapsulatedPluginTranslations = pluginTranslations; | ||
}); | ||
utilExt.merge(translations, encapsulatedPluginTranslations); | ||
} | ||
try { | ||
} catch (e) { | ||
process.error(error.message, { | ||
plugin: plugin.name, | ||
dir: plugin.i18nDirectory, | ||
file: translationFile | ||
}); | ||
callback(new Error('An error occured while loading a translations dictionary')); | ||
return; | ||
} | ||
// Main application | ||
if (i === 0 && (translationFile || languageFile || defaultTranslationFile)) | ||
translations = require( | ||
i18nDirectories[i] + '/' + (translationFile || languageFile || defaultTranslationFile)); | ||
callback(); | ||
}); | ||
// Plugins | ||
else if (translationFile || languageFile || defaultTranslationFile) { | ||
translations = translations || {}; | ||
utilExt.merge(translations, require( | ||
i18nDirectories[i] + '/' + (translationFile || languageFile || defaultTranslationFile))); | ||
} | ||
}); | ||
} | ||
}); | ||
} catch (e) { | ||
process.logger.error('An error occured while loading a translations dictionary with message : ' + e.message); | ||
} | ||
i++; | ||
}); | ||
callback(translations); | ||
async.parallel(asyncFunctions, function(error, results) { | ||
callback(error, translations); | ||
}); | ||
}; |
@@ -14,3 +14,3 @@ 'use strict'; | ||
/** | ||
* Adds a new logger. | ||
* Adds a new file logger. | ||
* | ||
@@ -23,6 +23,7 @@ * Added loggers will also log to process standard output in development mode (not in production). | ||
* var conf = { | ||
* 'fileName' : '/tmp/openveo/logs/openveo.log', | ||
* 'level' : 'debug', | ||
* 'maxFileSize' : 1048576, | ||
* 'maxFiles' : 2 | ||
* 'fileName' : '/tmp/openveo/logs/openveo.log', // File to log to | ||
* 'level' : 'debug', // Debug level | ||
* 'maxFileSize' : 1048576, // Maximum file size (in bytes) | ||
* 'maxFiles' : 2, // Maximum number of archived files | ||
* 'console': false // Deactivate logs in standard output | ||
* }; | ||
@@ -62,3 +63,3 @@ * | ||
// Remove default logger, which log to the standard output, in production environment | ||
if (process.env.NODE_ENV == 'production') | ||
if (process.env.NODE_ENV == 'production' || conf.console === false) | ||
winston.loggers.get(name).remove(winston.transports.Console); | ||
@@ -65,0 +66,0 @@ |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* The EntityModel provides basic CRUD (**C**read **R**ead **U**pdate **D**elete) operations on entities.<br/> | ||
* The EntityModel provides basic CRUD (**C**reate **R**ead **U**pdate **D**elete) operations on entities.<br/> | ||
* All entities models must extend EntityModel. EntityModel must not be used directly. Use one of its sub class | ||
@@ -32,3 +32,3 @@ * instead. | ||
* // Initialize the entity model with a dedicated provider | ||
* api.EntityModel.prototype.init.call(this, new CustomProvider(api.applicationStorage.getDatabase())); | ||
* api.EntityModel.call(this, new CustomProvider(api.applicationStorage.getDatabase())); | ||
* | ||
@@ -52,16 +52,5 @@ * } | ||
* @constructor | ||
*/ | ||
function EntityModel() { | ||
} | ||
module.exports = EntityModel; | ||
/** | ||
* Initializes an EntityModel with a provider. | ||
* | ||
* @method init | ||
* @protected | ||
* @param {EntityProvider} provider The entity provider | ||
*/ | ||
EntityModel.prototype.init = function(provider) { | ||
function EntityModel(provider) { | ||
this.provider = provider; | ||
@@ -71,4 +60,6 @@ | ||
throw new Error('An EntityModel needs a provider'); | ||
}; | ||
} | ||
module.exports = EntityModel; | ||
/** | ||
@@ -80,2 +71,3 @@ * Gets a single entity by its id. | ||
* @param {String} id The entity id | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
@@ -85,4 +77,4 @@ * - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
EntityModel.prototype.getOne = function(id, callback) { | ||
this.provider.getOne(id, callback); | ||
EntityModel.prototype.getOne = function(id, filter, callback) { | ||
this.provider.getOne(id, filter, callback); | ||
}; | ||
@@ -95,2 +87,3 @@ | ||
* @async | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
@@ -100,21 +93,7 @@ * - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
EntityModel.prototype.get = function(callback) { | ||
this.provider.get(callback); | ||
EntityModel.prototype.get = function(filter, callback) { | ||
this.provider.get(filter, callback); | ||
}; | ||
/** | ||
* Gets a list of filtered entities | ||
* | ||
* @method getByFilter | ||
* @async | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Object** The entity | ||
*/ | ||
EntityModel.prototype.getByFilter = function(filter, callback) { | ||
this.provider.getByFilter(filter, callback); | ||
}; | ||
/** | ||
* Gets an ordered list of entities by page. | ||
@@ -147,9 +126,10 @@ * | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Number** The total amount of items inserted | ||
* - **Object** The added entity | ||
*/ | ||
EntityModel.prototype.add = function(data, callback) { | ||
data.id = shortid.generate(); | ||
data.id = data.id || shortid.generate(); | ||
this.provider.add(data, function(error, insertCount, documents) { | ||
if (callback) | ||
callback(error, documents[0]); | ||
callback(error, insertCount, documents[0]); | ||
}); | ||
@@ -156,0 +136,0 @@ }; |
@@ -77,8 +77,21 @@ 'use strict'; | ||
/** | ||
* Indicates that the plugin is fully loaded. | ||
* Offers the possibility to initialize the plugin. | ||
* | ||
* This won't be called by the Web Service. | ||
* A plugin may want, for example, to use this method to create indexes for its collections. | ||
* | ||
* @method init | ||
* @async | ||
* @param {Function} callback Function to call when it's done with : | ||
* - **Error** An error if something went wrong, null otherwise | ||
*/ | ||
Plugin.prototype.init = null; | ||
/** | ||
* Indicates that the plugin is fully loaded in application process and can be started. | ||
* | ||
* @method start | ||
* @async | ||
* @param {Function} callback Function to call when it's done with : | ||
* - **Error** An error if something went wrong, null otherwise | ||
*/ | ||
Plugin.prototype.start = null; |
@@ -29,3 +29,3 @@ 'use strict'; | ||
* // Initialize the entity provider with collection "customCollection" | ||
* api.EntityProvider.prototype.init.call(this, database, 'customCollection'); | ||
* api.EntityProvider.call(this, database, 'customCollection'); | ||
* | ||
@@ -51,16 +51,2 @@ * } | ||
function EntityProvider(database, collection) { | ||
this.init(database, collection); | ||
} | ||
module.exports = EntityProvider; | ||
/** | ||
* Initializes an EntityProvider with a collection. | ||
* | ||
* @method init | ||
* @protected | ||
* @param {Database} database The database to interact with | ||
* @param {String} collection The collection name where entities are stored | ||
*/ | ||
EntityProvider.prototype.init = function(database, collection) { | ||
this.database = database; | ||
@@ -74,4 +60,5 @@ this.collection = collection; | ||
throw new Error('Database must be an of type Database'); | ||
} | ||
}; | ||
module.exports = EntityProvider; | ||
@@ -84,2 +71,3 @@ /** | ||
* @param {String} id The entity id | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
@@ -89,6 +77,7 @@ * - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
EntityProvider.prototype.getOne = function(id, callback) { | ||
this.database.get(this.collection, { | ||
id: id | ||
}, | ||
EntityProvider.prototype.getOne = function(id, filter, callback) { | ||
if (!filter) filter = {}; | ||
filter.id = id; | ||
this.database.get(this.collection, filter, | ||
{ | ||
@@ -106,21 +95,2 @@ _id: 0 | ||
/** | ||
* Gets an entity filter by custom filter. | ||
* | ||
* @method getByFilter | ||
* @async | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Object** The entity | ||
*/ | ||
EntityProvider.prototype.getByFilter = function(filter, callback) { | ||
this.database.get(this.collection, filter, null, 1, function(error, entities) { | ||
if (entities && entities.length) | ||
callback(error, entities[0]); | ||
else | ||
callback(error); | ||
}); | ||
}; | ||
/** | ||
* Gets an ordered list of entities by page. | ||
@@ -148,2 +118,3 @@ * | ||
* @async | ||
* @param {Object} filter A MongoDB filter | ||
* @param {Function} callback The function to call when it's done | ||
@@ -153,4 +124,4 @@ * - **Error** The error if an error occurred, null otherwise | ||
*/ | ||
EntityProvider.prototype.get = function(callback) { | ||
this.database.get(this.collection, null, { | ||
EntityProvider.prototype.get = function(filter, callback) { | ||
this.database.get(this.collection, filter, { | ||
_id: 0 | ||
@@ -175,3 +146,3 @@ }, | ||
this.database.insert(this.collection, datas, callback || function(error, result) { | ||
this.database.insert(this.collection, datas, callback || function(error) { | ||
if (error) | ||
@@ -201,3 +172,3 @@ process.logger.error('Error while inserting entities with message : ' + | ||
this.database.update(this.collection, filter, data, callback || function(error, result) { | ||
this.database.update(this.collection, filter, data, callback || function(error) { | ||
if (error) | ||
@@ -227,3 +198,3 @@ process.logger.error('Error while updating entities message : ' + | ||
this.database.remove(this.collection, filter, callback || function(error, result) { | ||
this.database.remove(this.collection, filter, callback || function(error) { | ||
if (error) | ||
@@ -250,3 +221,3 @@ process.logger.error('Error while removing entities with message : ' + error.message, ids); | ||
this.database.removeProp(this.collection, property, filter, callback || function(error, result) { | ||
this.database.removeProp(this.collection, property, filter, callback || function(error) { | ||
if (error) | ||
@@ -257,1 +228,26 @@ process.logger.error('Error while removing property from entities(s) with message : ' + | ||
}; | ||
/** | ||
* Increase an entity. | ||
* | ||
* If the entity has the property "locked", it won't be increased. | ||
* | ||
* @method increase | ||
* @async | ||
* @param {String} id The id of the entity to update | ||
* @param {Object} data Object which key is the parameter to increase and value, amount of increase/decrease | ||
* - Ex: {views: 56, priority: -5} | ||
* @param {Function} callback The function to call when it's done | ||
* - **Error** The error if an error occurred, null otherwise | ||
* - **Number** The number of updated items | ||
*/ | ||
EntityProvider.prototype.increase = function(id, data, callback) { | ||
var filter = {}; | ||
filter['locked'] = {$ne: true}; | ||
filter['id'] = id; | ||
this.database.increase(this.collection, filter, data, callback || function(error) { | ||
if (error) | ||
process.logger.error('Error while increasing entities message : ' + | ||
error.message, data); | ||
}); | ||
}; |
263
lib/util.js
@@ -30,2 +30,8 @@ 'use strict'; | ||
module.exports.merge = function(object1, object2) { | ||
if (!object2) | ||
return object1; | ||
if (!object1) | ||
return object2; | ||
for (var property in object2) { | ||
@@ -40,4 +46,3 @@ | ||
object1[property] = this.merge(object1[property], object2[property]); | ||
} | ||
else | ||
} else | ||
object1[property] = object2[property]; | ||
@@ -58,8 +63,8 @@ | ||
/** | ||
* Merges two arrays without any duplicated value. | ||
* Makes union of two arrays. | ||
* | ||
* @method joinArray | ||
* @param {Array} [array1] A JavaScript array | ||
* @param {Array} [array2] A JavaScript array | ||
* @return {Array} array | ||
* @param {Array} [array1] An array | ||
* @param {Array} [array2] An array | ||
* @return {Array} The union of the two arrays | ||
*/ | ||
@@ -72,2 +77,15 @@ module.exports.joinArray = function(array1, array2) { | ||
/** | ||
* Makes intersection of two arrays. | ||
* | ||
* @method intersectArray | ||
* @param {Array} [array1] An array | ||
* @param {Array} [array2] An array | ||
* @return {Array} The intersection of the two arrays | ||
*/ | ||
module.exports.intersectArray = function(array1, array2) { | ||
return array2.filter(function(item) { | ||
return array1.indexOf(item) >= 0; | ||
}); | ||
}; | ||
@@ -86,1 +104,234 @@ /** | ||
}; | ||
/** | ||
* Checks if a value is isContained into another comparing primitive types. | ||
* | ||
* All values in expectedValue must be found in value to pass the test. | ||
* | ||
* @method isContained | ||
* @param {Object|Number|String|Array} expectedValue The value expecting to be found in "value" | ||
* @return {Boolean} true if the expected value has been found in value | ||
*/ | ||
module.exports.isContained = function(expectedValue, value) { | ||
if (Object.prototype.toString.call(expectedValue) === '[object Array]') { | ||
if (Object.prototype.toString.call(value) !== '[object Array]') | ||
return false; | ||
for (var i = 0; i < expectedValue.length; i++) { | ||
if (!this.isContained(expectedValue[i], value[i])) | ||
return false; | ||
} | ||
} else if (Object.prototype.toString.call(expectedValue) === '[object Object]') { | ||
if (Object.prototype.toString.call(value) !== '[object Object]') | ||
return false; | ||
for (var property in expectedValue) { | ||
if (!this.isContained(expectedValue[property], value[property])) | ||
return false; | ||
} | ||
} else if (expectedValue !== value) | ||
return false; | ||
return true; | ||
}; | ||
/** | ||
* Validates first level object properties using the given validation description object. | ||
* | ||
* It helps validating that an object, coming from a request query string parameters correspond to the expected | ||
* type, if it has to be required, if it must be contained into a list of values etc. | ||
* | ||
* Available features by types : | ||
* - **string** | ||
* - **default** Specify a default value | ||
* - **required** Boolean to indicate if the value is required (if default is specified, value will always be set) | ||
* - **in** Specify an array of strings to validate that the value is inside this array | ||
* - **number** | ||
* - **default** Specify a default value | ||
* - **required** Boolean to indicate if the value is required (if default is specified, value will always be set) | ||
* - **in** Specify an array of numbers to validate that the value is inside this array | ||
* - **gt** Specify a number to validate that the value is greater than this number | ||
* - **lt** Specify a number to validate that the value is lesser than this number | ||
* - **gte** Specify a number to validate that the value is greater or equal to this number | ||
* - **lte** Specify a number to validate that the value is lesser or equal to this number | ||
* - **array<string>** | ||
* - **required** Boolean to indicate if the value is required (an empty array is not an error) | ||
* - **array<number>** | ||
* - **required** Boolean to indicate if the value is required (an empty array is not an error) | ||
* - **date** | ||
* - **required** Boolean to indicate if the value is required | ||
* - **gt** Specify a date to validate that the value is greater than this date | ||
* - **lt** Specify a date to validate that the value is lesser than this date | ||
* - **gte** Specify a date to validate that the value is greater or equal to this date | ||
* - **lte** Specify a date to validate that the value is lesser or equal to this date | ||
* - **object** | ||
* - **default** Specify a default value | ||
* - **required** Boolean to indicate if the value is required (if default is specified, value will always be set) | ||
* | ||
* @example | ||
* | ||
* // Get util | ||
* var util = require('@openveo/api').util; | ||
* | ||
* // Validate parameters | ||
* var params = util.shallowValidateObject({ | ||
* myStringProperty: 'my value', | ||
* myNumberProperty: 25, | ||
* myArrayStringProperty: ['value1', 'value2'] | ||
* myArrayNumberProperty: [10, 5] | ||
* myDateProperty: '02/25/2016' | ||
* myObjectProperty: {firstKey: 'firstValue'} | ||
* }, { | ||
* myStringProperty: {type: 'string', required: true, default: 'default', in: ['my value', 'value']}, | ||
* myNumberProperty: {type: 'number', required: true, default: 0, in: [0, 5, 10], gte: 0, lte: 5}, | ||
* myArrayStringProperty: {type: 'array<string>', required: true}, | ||
* myArrayNumberProperty: {type: 'array<number>', required: true}, | ||
* myDateProperty: {type: 'date', required: true, gte: '02/20/2016', lte: '03/30/2016'} | ||
* myObjectProperty: {type: 'object', required: true} | ||
* }); | ||
* | ||
* console.log(params); | ||
* | ||
* @method shallowValidateObject | ||
* @param {Object} objectToAnalyze The object to analyze | ||
* @param {Object} validationDescription The validation description object | ||
* @return {Object} A new object with the list of properties as expected | ||
*/ | ||
module.exports.shallowValidateObject = function(objectToAnalyze, validationDescription) { | ||
var dateFormat = /(\d{2})[-\/](\d{2})[-\/](\d{4})/; | ||
var properties = {}; | ||
// Iterate through the list of expected properties | ||
for (var name in validationDescription) { | ||
var expectedProperty = validationDescription[name]; | ||
var value = objectToAnalyze[name]; | ||
if (expectedProperty) { | ||
// This property was expected | ||
// Options | ||
var defaultValue = expectedProperty.default || null; | ||
var required = expectedProperty.required || false; | ||
var inside = expectedProperty.in || null; | ||
var gt = expectedProperty.gt || null; | ||
var lt = expectedProperty.lt || null; | ||
var gte = expectedProperty.gte || null; | ||
var lte = expectedProperty.lte || null; | ||
switch (expectedProperty.type) { | ||
case 'string': | ||
value = value !== undefined ? String(value) : defaultValue; | ||
if (inside && inside.indexOf(value) < 0) | ||
throw new Error('Property ' + name + ' must be one of ' + inside.join(', ')); | ||
break; | ||
case 'number': | ||
value = value !== undefined ? parseInt(value) : defaultValue; | ||
value = value || defaultValue || null; | ||
if (gt && value <= gt) | ||
throw new Error('Property ' + name + ' must be greater than ' + gt); | ||
if (lt && value >= lt) | ||
throw new Error('Property ' + name + ' must be lesser than ' + lt); | ||
if (gte && value < gte) | ||
throw new Error('Property ' + name + ' must be greater or equal to ' + gte); | ||
if (lte && value > lte) | ||
throw new Error('Property ' + name + ' must be lesser or equal to ' + lte); | ||
if (inside && inside.indexOf(value) < 0) | ||
throw new Error('Property ' + name + ' must be one of ' + inside.join(', ')); | ||
break; | ||
case 'array<string>': | ||
case 'array<number>': | ||
var arrayType = /array<([^>]*)>/.exec(expectedProperty.type)[1]; | ||
if (typeof value === 'string' || typeof value === 'number') { | ||
value = arrayType === 'string' ? String(value) : parseInt(value); | ||
value = value ? [value] : null; | ||
} else if (Object.prototype.toString.call(value) === '[object Array]') { | ||
var arrayValues = []; | ||
for (var i = 0; i < value.length; i++) { | ||
var convertedValue = arrayType === 'string' ? String(value[i]) : parseInt(value[i]); | ||
if (convertedValue) | ||
arrayValues.push(convertedValue); | ||
} | ||
value = arrayValues.length ? arrayValues : null; | ||
} else | ||
value = null; | ||
break; | ||
case 'date': | ||
var date; | ||
if (!value) | ||
value = null; | ||
else { | ||
if (typeof value === 'string') { | ||
// Convert literal date into Date object | ||
var dateChunks = dateFormat.exec(value); | ||
if (dateChunks && dateChunks.length === 4) | ||
date = new Date(value).getTime(); | ||
else | ||
date = null; | ||
} else if (Object.prototype.toString.call(value) === '[object Date]') { | ||
// Already a Date object | ||
date = value.getTime(); | ||
} | ||
if (date) | ||
value = date; | ||
if (gt) { | ||
var gtDate = typeof gt === 'object' ? gt : new Date(gt); | ||
if (value <= gtDate.getTime()) | ||
throw new Error('Property ' + name + ' must be greater than ' + gtDate.toString()); | ||
} | ||
if (lt) { | ||
var ltDate = typeof lt === 'object' ? lt : new Date(lt); | ||
if (value >= ltDate.getTime()) | ||
throw new Error('Property ' + name + ' must be lesser than ' + ltDate.toString()); | ||
} | ||
if (gte) { | ||
var gteDate = typeof gte === 'object' ? gte : new Date(gte); | ||
if (value < gteDate.getTime()) | ||
throw new Error('Property ' + name + ' must be greater or equal to ' + gteDate.toString()); | ||
} | ||
if (lte) { | ||
var lteDate = typeof lte === 'object' ? lte : new Date(lte); | ||
if (value > lteDate.getTime()) | ||
throw new Error('Property ' + name + ' must be lesser or equal to ' + lteDate.toString()); | ||
} | ||
} | ||
break; | ||
case 'object': | ||
var valueType = Object.prototype.toString.call(value); | ||
value = value !== undefined && valueType ? value : defaultValue; | ||
break; | ||
default: | ||
value = null; | ||
} | ||
if (required && (value === null || typeof value === 'undefined')) | ||
throw new Error('Property ' + name + ' required'); | ||
else if (value !== null && typeof value !== 'undefined') | ||
properties[name] = value; | ||
} | ||
} | ||
return properties; | ||
}; |
{ | ||
"name": "@openveo/api", | ||
"version": "2.0.0", | ||
"version": "3.0.0", | ||
"description": "API for OpenVeo plugins", | ||
@@ -22,17 +22,17 @@ "keywords": [ | ||
"dependencies": { | ||
"tar": "1.0.3" | ||
"tar": "2.2.1" | ||
}, | ||
"devDependencies": { | ||
"chai": ">=1.10.0", | ||
"glob": ">=5.0.12", | ||
"grunt": ">=0.4.5", | ||
"grunt-contrib-yuidoc": ">=0.8.0", | ||
"grunt-eslint": ">=17.2.0", | ||
"grunt-extend-config": ">=0.9.2", | ||
"grunt-gh-pages": ">=0.10.0", | ||
"grunt-init": ">=0.3.2", | ||
"grunt-mocha-test": ">=0.12.7", | ||
"mocha": ">=2.1.0", | ||
"pre-commit": ">=1.1.1", | ||
"yuidoc-theme-blue": ">=0.1.9" | ||
"chai": "3.5.0", | ||
"glob": "7.0.3", | ||
"grunt": "0.4.5", | ||
"grunt-contrib-yuidoc": "1.0.0", | ||
"grunt-eslint": "18.1.0", | ||
"grunt-extend-config": "0.9.3", | ||
"grunt-gh-pages": "1.1.0", | ||
"grunt-init": "0.3.2", | ||
"grunt-mocha-test": "0.12.7", | ||
"mocha": "2.4.5", | ||
"pre-commit": "1.1.2", | ||
"yuidoc-theme-blue": "0.1.9" | ||
}, | ||
@@ -39,0 +39,0 @@ "scripts": { |
@@ -19,3 +19,3 @@ # OpenVeo API | ||
Documentation is available on [Github pages](http://veo-labs.github.io/openveo-api/2.0.0). | ||
Documentation is available on [Github pages](http://veo-labs.github.io/openveo-api/3.0.0). | ||
@@ -22,0 +22,0 @@ # Contributors |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
135196
33
2848
6