fullcontact
Version:
Fullcontact API bindings
303 lines (258 loc) • 7.14 kB
JavaScript
'use strict';
var request = require('request')
, qs = require('qs');
var slice = Array.prototype.slice;
/**
* Create new FullContact instance.
*
* @constructor
* @param {String} api The API key for the Full Contact service.
* @api public
*/
function FullContact(api) {
if (!(this instanceof FullContact)) {
return new FullContact(api);
}
this.key = api; // API key
this.version = 'v2'; // API version
this.remaining = 0; // How many API calls are remaining
this.ratelimit = 0; // The amount of API calls allowed
this.ratereset = 0; // In how many seconds is the rate limit reset
this.queueing = false; // Should we be queueing requests
this.requests = []; // Stores all queued commands
if (!this.key) {
throw new Error('Missing API key');
}
}
/**
* Process the requests before sending, queueing, or what evering
*
* @param {Mixed} api Reference to the Person/WhatEver API.
* @param {Object} query Query string object that should be send.
* @param {Object} args Arguments.
* @api private
*/
FullContact.prototype.process = function req(api, query, args) {
query.apiKey = query.apiKey || this.key;
//
// Add some addition properties
//
if (args.queue) {
query.queue = args.queue;
}
if (args.casing) {
query.casing = args.casing;
}
if (args.population) {
query.includeZeroPopulation = !!args.population;
}
if (args.webhookUrl) {
query.webhookUrl = args.webhookUrl;
}
if (args.webhookId) {
query.webhookId = args.webhookId;
}
//
// The packet that is send to the server or queued when we are in queuing
// mode.
//
var packet = {
method: 'GET',
uri: args.endpoint || api.endpoint,
qs: query
};
if (this.queueing) {
return this.queue(packet, args);
}
return this.request(packet, args);
};
/**
* Send a GET request to the FullContact servers using given packet.
*
* @param {Object} packet Request packet.
* @param {Object} args API arguments.
* @api private
*/
FullContact.prototype.request = function req(packet, args) {
var fn = args.fn
, self = this;
request(packet, function requested(err, res, body) {
if (err) {
return fn(err);
}
self.ratereset = +res.headers['x-rate-limit-reset'] || self.ratereset;
self.ratelimit = +res.headers['x-rate-limit-limit'] || self.ratelimit;
self.remaining = +res.headers['x-rate-limit-remaining'] || self.remaining;
//
// Parse response to JSON.
//
if (typeof body === 'string') {
try { body = JSON.parse(body); }
catch (e) {
return fn(new Error('Failed to parse API response (' + e.message + ')'));
}
}
//
// Handle API errors, FullContact adds a "status" key to the JSON response
// it will have a "message" property when it's a failed request so we can
// leverage that to return a nice error.
//
if (body.status !== 200 && body.status !== 202) {
err = new Error(body.message);
err.status = body.status;
return fn(err);
}
fn(undefined, body);
});
return this;
};
/**
* Start a multi/batch request to the API.
*
* @api public
*/
FullContact.prototype.multi = function multi() {
var queued = new FullContact(this.key);
//
// Force queueing mode on the newly generated API
//
queued.queueing = true;
return queued;
};
/**
* Queue up requests so they can be send as a batch.
*
* @param {Object} packet The object that would be send using .request
* @param {Object} args The arguments;
* @api private
*/
FullContact.prototype.queue = function queue(packet, args) {
this.requests.push({
url: packet.uri + '?' + qs.stringify(packet.qs),
fn: args.fn
});
return this;
};
/**
* Send all the batched requests to the server.
*
* @param {Function} fn The callback.
* @api public
*/
FullContact.prototype.exec = function exec(fn) {
fn = fn || function noop() {};
var requests = this.requests.splice(0);
/**
* Handle errors easily and ensure that all callbacks are called that were
* queued in the process.
*
* @param {Error} err The exception that occured
*/
function bailout(err) {
requests.forEach(function cb(data) {
if (data.fn) {
data.fn(err);
}
});
fn(err);
}
//
// We are no longer queing requests.
//
this.queueing = false;
request({
method: 'POST',
uri: 'https://api.fullcontact.com/' + this.version + '/batch.json',
qs: { apiKey: this.key },
json: {
requests: requests.map(function urlsonly(data) {
return data.url;
})
}
}, function requested(err, res, body) {
if (err) {
return bailout(err);
}
fn(err, body.responses);
});
};
/**
* Parse the given arguments because we don't want to do an optional queue check
* for every single API endpoint.
*
* @param {Arguments} args Arguments
* @returns {Object}
* @api private
*/
FullContact.prototype.args = function parser(args) {
var optional = slice.call(arguments, 1);
args = slice.call(args, 0);
return optional.reduce(function optional(data, key, index) {
data[key] = args[index];
return data;
}, { value: args.shift(), fn: args.pop() });
};
/**
* Define an lazyload new API's.
*
* @param {Object} where Where should we define the property
* @param {String} name The name of the property
* @param {Function} fn The function that returns the new value
* @api private
*/
FullContact.define = function define(where, name, fn) {
Object.defineProperty(where, name, {
configurable: true,
get: function get() {
return Object.defineProperty(this, name, {
value: fn.call(this)
})[name];
},
set: function set(value) {
return Object.defineProperty(this, name, {
value: value
})[name];
}
});
};
/**
* Have a Node API compatible client interface.
*
* @param {String} api The API key for full contact
* @returns {FullContact}
* @api public
*/
FullContact.createClient = function createClient(api) {
return new FullContact(api);
};
//
// Expose the endpoints.
//
FullContact.Location = require('./endpoints/location');
FullContact.Person = require('./endpoints/person');
FullContact.Email = require('./endpoints/email');
FullContact.Name = require('./endpoints/name');
FullContact.Company = require('./endpoints/company');
//
// Lazy load the various of endpoints so they only get initialized when we
// actually need them.
//
FullContact.define(FullContact.prototype, 'location', function define() {
return new FullContact.Location(this);
});
FullContact.define(FullContact.prototype, 'email', function define() {
return new FullContact.Email(this);
});
FullContact.define(FullContact.prototype, 'person', function define() {
return new FullContact.Person(this);
});
FullContact.define(FullContact.prototype, 'name', function define() {
return new FullContact.Name(this);
});
FullContact.define(FullContact.prototype, 'company', function define() {
return new FullContact.Company(this);
});
//
// Expose the FullContact API.
//
module.exports = FullContact;