Commit 752f0548 by Ben Drucker

Add watchlist API support

Closes #7
parent 001b6908
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
"eqeqeq": true, "eqeqeq": true,
"immed": true, "immed": true,
"indent": 2, "indent": 2,
"latedef": true,
"newcap": true, "newcap": true,
"noarg": true, "noarg": true,
"quotmark": "single", "quotmark": "single",
......
...@@ -12,52 +12,52 @@ var pkg = require('../package.json'); ...@@ -12,52 +12,52 @@ var pkg = require('../package.json');
function ClearbitClient (config) { function ClearbitClient (config) {
config = config || {}; config = config || {};
assert(this instanceof ClearbitClient, 'Client must be called with new'); assert(this instanceof ClearbitClient, 'Client must be called with new');
assert(!!config.key, 'An API key must be provided');
this.key = config.key;
this.key = config.key || process.env.CLEARBIT_KEY;
assert(!!this.key, 'An API key must be provided');
this.Company = require('./company').Company(this);
this.Person = require('./person').Person(this); this.Person = require('./person').Person(this);
this.PersonCompany = require('./person').PersonCompany(this); this.PersonCompany = require('./person').PersonCompany(this);
this.Company = require('./company')(this); this.Watchlist = require('./watchlist').Watchlist(this);
this.WatchlistEntity = require('./watchlist').WatchlistEntity(this);
this.WatchlistIndividual = require('./watchlist').WatchlistIndividual(this);
} }
var base = 'https://%s%s.clearbit.com/v%s'; var ENDPOINT = 'https://%s%s.clearbit.com/v%s';
ClearbitClient.prototype.base = function (options) {
ClearbitClient.prototype.endpoint = function (options) {
options = _.defaults(options, { options = _.defaults(options, {
version: '1', version: '1',
stream: false stream: false
}); });
assert(options.api, 'An API must be specified'); assert(options.api, 'An API must be specified');
return util.format.apply(util, [
base, return util.format(
ENDPOINT,
options.api, options.api,
options.stream ? '-stream' : '', options.stream ? '-stream' : '',
options.version options.version
]); );
}; };
ClearbitClient.prototype.url = function (options) { ClearbitClient.prototype.url = function (options) {
_.defaults(options, { _.defaults(options, {
path: '' path: ''
}); });
return this.base(options) + options.path; return this.endpoint(options) + options.path;
}; };
function generateQuery () {
var query = _.omit(_.extend.apply(_, [{}].concat([].slice.apply(arguments))), _.isUndefined);
return _.isEmpty(query) ? undefined : query;
}
ClearbitClient.prototype.request = function (options) { ClearbitClient.prototype.request = function (options) {
options = _.defaults(options, { options = _.defaults(options, {
method: 'get', method: 'get'
query: {}
}); });
return needle.requestAsync( return needle.requestAsync(
options.method, options.method,
this.url(options), this.url(options),
generateQuery({ options.query,
webhook_id: options.webhook_id
}, options.query),
{ {
timeout: options.stream ? 60000 : 10000, timeout: options.stream ? 60000 : 10000,
username: this.key, username: this.key,
......
...@@ -4,19 +4,20 @@ var assert = require('assert'); ...@@ -4,19 +4,20 @@ var assert = require('assert');
var resource = require('./resource'); var resource = require('./resource');
var _ = require('lodash'); var _ = require('lodash');
module.exports = resource.create('Company', { exports.Company = resource.create('Company', {api: 'company'})
api: 'company', .extend({
path: '/companies/domain/<%= domain %>' flag: function(options){
}) return this.constructor.post('/companies/' + this.id + '/flag', options);
.on('preFind', function (options) { }
assert(options.domain, 'A domain must be provided'); },
}).include({ {
flag: function(params, options){ find: function (options) {
return this.client.request(_.extend({ options = options || {};
api: this._options.api, assert(options.domain, 'A domain must be provided');
method: 'post',
path: _.template('/companies/<%= id %>/flag', this), return this.get(
query: params || {} '/companies/domain/' + options.domain,
}, options)); _.omit(options, 'domain')
} );
}); }
});
...@@ -4,30 +4,33 @@ var assert = require('assert'); ...@@ -4,30 +4,33 @@ var assert = require('assert');
var resource = require('./resource'); var resource = require('./resource');
var _ = require('lodash'); var _ = require('lodash');
function requireEmail (options) { exports.Person = resource.create('Person', {api: 'person'})
assert(options.email, 'An email must be provided'); .extend({
} flag: function(options){
return this.constructor.post('/people/' + this.id + '/flag', options);
}
},
{
find: function(options){
options = options || {};
assert(options.email, 'An email must be provided');
exports.Person = resource.create('Person', { return this.get(
api: 'person', '/people/email/' + options.email,
path: '/people/email/<%= email %>', _.omit(options, 'email')
queryKeys: 'subscribe' );
}) }
.on('preFind', requireEmail) });
.include({
flag: function(params, options){
return this.client.request(_.extend({
api: this._options.api,
method: 'post',
path: _.template('/people/<%= id %>/flag', this),
query: params || {}
}, options));
}
});
exports.PersonCompany = resource.create('PersonCompany', { exports.PersonCompany = resource.create('PersonCompany', {api: 'person'})
api: 'person', .extend(null, {
path: '/combined/email/<%= email %>', find: function(options){
queryKeys: 'subscribe' options = options || {};
}) assert(options.email, 'An email must be provided');
.on('preFind', requireEmail);
return this.get(
'/combined/email/' + options.email,
_.omit(options, 'email')
);
}
});
'use strict'; 'use strict';
var createError = require('create-error'); var createError = require('create-error');
var EventEmitter = require('events').EventEmitter; var _ = require('lodash');
var _ = require('lodash');
var Promise = require('bluebird');
function isQueued (err) {
return err.type === 'queued';
}
function isUnknownRecord (err) {
return err.type === 'unknown_record';
}
function ClearbitResource (data) { function ClearbitResource (data) {
_.extend(this, data); _.extend(this, data);
} }
ClearbitResource.find = Promise.method(function (options) { ClearbitResource.get = function (path, options) {
options = options || /* istanbul ignore next */ {}; options = _.extend({
this.emit('preFind', options); path: path,
return this.client.request(_.extend({ method: 'get',
api: this._options.api, query: extractParams(options)
path: this._options.template(options), }, this.options, options);
query: _.pick(options, this._options.queryKeys)
}, options))
.bind(this)
.then(function (data) {
return new this(
_.extend({}, data, {
_options: this._options,
client: this.client
})
);
})
.catch(isQueued, function () {
throw new this.QueuedError(this._name + ' lookup queued');
})
.catch(isUnknownRecord, function () {
throw new this.NotFoundError(this._name + ' not found');
});
});
function createErrors (name) { return this.client.request(options)
return { .bind(this)
NotFoundError: createError(name + 'NotFoundError'), .then(cast)
QueuedError: createError(name + 'QueuedError') .catch(isQueued, function () {
}; throw new this.QueuedError(this.name + ' lookup queued');
} })
.catch(isUnknownRecord, function () {
throw new this.NotFoundError(this.name + ' not found');
});
};
ClearbitResource.post = function (path, options) {
options = _.extend({
path: path,
method: 'post',
query: extractParams(options)
}, this.options, options);
return this.client.request(options)
.bind(this)
.then(cast)
.catch(isUnknownRecord, function () {
throw new this.NotFoundError(this.name + ' not found');
});
};
exports.create = function (name, options) { exports.create = function (name, options) {
var Resource = function () { var Resource = function () {
ClearbitResource.apply(this, arguments); ClearbitResource.apply(this, arguments);
}; };
_.extend(Resource, new EventEmitter(), EventEmitter.prototype, ClearbitResource, createErrors(name), { _.extend(Resource, ClearbitResource, createErrors(name), {
_name: name, name: name,
_options: _.extend({}, options, { options: options
template: _.template(options.path)
})
}); });
return _.extend(function (client) { return _.extend(function (client) {
...@@ -67,14 +56,41 @@ exports.create = function (name, options) { ...@@ -67,14 +56,41 @@ exports.create = function (name, options) {
}); });
}, },
{ {
on: function () { extend: function (proto, ctor) {
Resource.on.apply(Resource, arguments); _.extend(Resource.prototype, proto);
return this; _.extend(Resource, ctor);
},
include: function (props) {
_.extend(Resource.prototype, props);
return this; return this;
} }
}); });
}; };
function cast (data) {
/* jshint validthis:true */
return !Array.isArray(data) ? new this(data) : data.map(function (result) {
return new this(result);
}, this);
}
function isQueued (err) {
return err.type === 'queued';
}
function isUnknownRecord (err) {
return err.type === 'unknown_record';
}
function createErrors (name) {
return {
NotFoundError: createError(name + 'NotFoundError'),
QueuedError: createError(name + 'QueuedError')
};
}
function extractParams (options) {
var params = _.omit(options || {},
'path', 'method', 'params',
'client', 'api', 'stream'
);
return _.isEmpty(params) ? null : params;
}
'use strict';
var resource = require('./resource');
exports.Watchlist = resource.create('Watchlist', {
api: 'watchlist'
})
.extend(null, {
search: function(options) {
return this.post('/search/all', options);
}
});
exports.WatchlistIndividual = resource.create('WatchlistIndividual', {
api: 'watchlist'
})
.extend(null, {
search: function(options) {
return this.post('/search/individuals', options);
}
});
exports.WatchlistEntity = resource.create('WatchlistEntity', {
api: 'watchlist'
})
.extend(null, {
search: function(options) {
return this.post('/search/entities', options);
}
});
...@@ -34,22 +34,22 @@ describe('Client', function () { ...@@ -34,22 +34,22 @@ describe('Client', function () {
}); });
describe('#base', function () { describe('#endpoint', function () {
it('requires an API', function () { it('requires an API', function () {
expect(client.base.bind(client, {})) expect(client.endpoint.bind(client, {}))
.to.throw(/API must be specified/); .to.throw(/API must be specified/);
}); });
it('can generate the default base', function () { it('can generate the default endpoint', function () {
expect(client.base({ expect(client.endpoint({
api: 'person' api: 'person'
})) }))
.to.equal('https://person.clearbit.com/v1'); .to.equal('https://person.clearbit.com/v1');
}); });
it('can generate a streaming base', function () { it('can generate a streaming endpoint', function () {
expect(client.base({ expect(client.endpoint({
api: 'person', api: 'person',
stream: true stream: true
})) }))
...@@ -57,7 +57,7 @@ describe('Client', function () { ...@@ -57,7 +57,7 @@ describe('Client', function () {
}); });
it('can set a custom version', function () { it('can set a custom version', function () {
expect(client.base({ expect(client.endpoint({
api: 'person', api: 'person',
version: '2' version: '2'
})) }))
......
[
{
"id": "6ffa17f3-2653-485f-9129-73bdaa88087a",
"name": {
"fullName": "Joe Schmo"
},
"addresses": [],
"list": "ofac_sdn",
"remarks": null,
"type": "entity"
}
]
'use strict';
var expect = require('chai').expect;
var nock = require('nock');
var Watchlist = require('../')('k').Watchlist;
describe('Watchlist', function () {
var mock;
before(function () {
mock = nock('https://watchlist.clearbit.com');
});
after(nock.cleanAll);
afterEach(function () {
mock.done();
});
describe('Watchlist#search', function () {
var watchlist = require('./fixtures/watchlist');
it('can search a watchlist by name', function () {
mock
.post('/v1/search/all')
.reply(200, watchlist);
return Watchlist.search({name: 'Joe'})
.then(function (watchlist) {
expect(watchlist[0])
.to.be.an.instanceOf(Watchlist)
.and.have.property('id', watchlist.id);
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment