diff --git a/config/configuration.js b/config/configuration.js index d191483..7c9034a 100644 --- a/config/configuration.js +++ b/config/configuration.js @@ -7,5 +7,5 @@ module.exports = { aliases: require('../config/json/aliases.json'), documentRelatedEndpoints: require('../config/json/document-related-endpoints.json'), - apiHost: 'https://api.anyfetch.com' + apiHost: process.env.API_HOST || 'https://api.anyfetch.com' }; \ No newline at end of file diff --git a/config/json/document-related-endpoints.json b/config/json/document-related-endpoints.json index a2f4e8c..88d715c 100644 --- a/config/json/document-related-endpoints.json +++ b/config/json/document-related-endpoints.json @@ -1,6 +1,14 @@ { - "getSimilar": { "endpoint": "/documents/{id}/similar" }, - "getRelated": { "endpoint": "/documents/{id}/related" }, - "getRaw": { "endpoint": "/documents/{id}/raw" }, - "getFile": { "endpoint": "/documents/{id}/file" } + "getSimilar": { + "endpoint": "/documents/{id}/similar" + }, + "getRelated": { + "endpoint": "/documents/{id}/related" + }, + "getRaw": { + "endpoint": "/documents/{id}/raw" + }, + "getFile": { + "endpoint": "/documents/{id}/file" + } } \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index dfee51f..17fcab8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -14,9 +14,15 @@ var accessToken = process.env.ACCESS_TOKEN; var anyfetchRequest = request(configuration.apiHost); +/** + * Determine if `f` is a function + */ +var isFunction = function(f) { + return !!(f && f.constructor && f.call && f.apply); +}; + /** * Will be used to automatically generate the simplest mapping functions - * @see api.anyfetch.com/config/routes.js */ var generateMapping = function(config, name) { // `config` extends the default descriptor @@ -37,13 +43,8 @@ var generateMapping = function(config, name) { * apiMapping(params, cb) */ return function apiMapping() { - var argErrorMessage = 'Argument error in ' + name + ':'; - // Convert arguments to an array - var args = []; - for (var i in arguments) { - args.push(arguments[i]); - } + var args = Array.prototype.slice.call(arguments); // We must support quite a lot of different call syntaxes var params = {}; @@ -52,8 +53,8 @@ var generateMapping = function(config, name) { // cb is always the last argument var cb = args.pop(); // cb must be a function - if (!(cb && cb.constructor && cb.call && cb.apply)) { - throw new Error(argErrorMessage + ' the last argument must be a function (callback)'); + if (!isFunction(cb)) { + throw new Error('Argument error in ' + name + ':' + ' the last argument must be a function (callback)'); } if (config.body) { @@ -61,7 +62,7 @@ var generateMapping = function(config, name) { // Make sure that every field of the body is accepted for(var key in body) { if(config.body.indexOf(key) === -1) { - return cb(new Error(argErrorMessage + ' the key ' + key + ' is not allowed in this request\'s body')); + return cb(new Error('Argument error in ' + name + ':' + ' the key ' + key + ' is not allowed in this request\'s body')); } } } @@ -71,7 +72,7 @@ var generateMapping = function(config, name) { // Make sure that every GET parameter is accepted for(var key in params) { if(config.params.indexOf(key) === -1) { - return cb(new Error(argErrorMessage + ' the key ' + key + ' is not allowed in this request\'s GET parameters')); + return cb(new Error('Argument error in ' + name + ':' + ' the key ' + key + ' is not allowed in this request\'s GET parameters')); } } } @@ -80,7 +81,7 @@ var generateMapping = function(config, name) { if(config.requireId) { var id = args.pop(); if(isNaN(id)) { - return cb(new Error('Argument error, the first parameter must be a numeric id')); + return cb(new Error('Argument error in ' + name + ':' + ' the first parameter must be a numeric id')); } // Substitute id parameter in endpoint string @@ -141,38 +142,52 @@ var generateAliases = function(mappingFunctions) { var generateAdvancedMappings = function(mappingFunctions) { // ----- Documents var getDocumentByIdCb = mappingFunctions.getDocumentById; - mappingFunctions.getDocumentById = function() { - // Invalid call syntax - if (arguments.length > 2 || isNaN(arguments[0])) { - // We assume the last argument is a callback - arguments[arguments.length - 1](new Error('Invalid call syntax')); - } - var id = arguments[0]; + + /** + * Support two call syntaxes: + * - Single step: getDocumentById(123, cb) + * - Two steps: getDocumentById(123).getRelated(cb) + */ + mappingFunctions.getDocumentById = function(id, cb) { // Single-step call syntax - // getDocumentById(123, cb) - if (arguments.length === 2) { + if (isFunction(cb)) { + if (isNaN(id)) { + return cb(new Error('Argument error in getDocumentById: the first argument must be a numeric id')); + } return getDocumentByIdCb(id, arguments[1]); } // Two-step call syntax - // getDocumentById(123).getRelated(cb) var secondStep = {}; for (var name in configuration.documentRelatedEndpoints) { - var opt = { - endpoint: configuration.documentRelatedEndpoints[name].endpoint.replace('{id}', id) + var opt = configuration.documentRelatedEndpoints[name]; + // Trick: "hardcode" id parameter in endpoint and proceed as usual + opt.endpoint = opt.endpoint.replace('{id}', id); + + secondStep[name] = function(){ + if (isNaN(id)) { + // We assume the last parameter is a callback + var cb = arguments[arguments.length - 1]; + return cb(new Error('Argument error in getDocumentById: the first argument must be a numeric id')); + } + + generateMapping(opt, name).apply(this, arguments); }; - secondStep[name] = generateMapping(opt, name); } - /** - * Add a file to a previously uploaded document - * - * @param {Object} Must at least contain a `file` key, which can either be a stream (e.g. `fs.createReadStream`) or a Buffer object. Can also contains a `contentType` key (for MIME type), and a `filename`. - * @param {Function} cb Callback with error if any. - * @Warning: unfortunately, due to the variety of Stream, we can't type-check, so unexpected errors will occur if you specify weird file parameters. - */ + /** + * Add a file to a previously uploaded document + * + * @param {Object} Must at least contain a `file` key, which can either be a stream (e.g. `fs.createReadStream`) or a Buffer object. Can also contains a `contentType` key (for MIME type), and a `filename`. + * @param {Function} cb Callback with error if any. + * @Warning: unfortunately, due to the variety of Stream, we can't type-check, so unexpected errors will occur if you specify weird file parameters. + */ secondStep.postFile = function(config, cb) { + if (isNaN(id)) { + return cb(new Error('Argument error in getDocumentById: the first argument must be a numeric id')); + } + if(typeof(config) === 'function') { config = config(); }