UNPKG

appnexus-node-api

Version:

Appnexus client for node.js

387 lines (359 loc) 11.9 kB
'use strict'; var http = require('http'), debug = require('debug')('appnexus-api'), util = require('util'), https = require('https'), appnexusUtil = require('./appnexus-util'), events = require('events'), RateLimiter = require('limiter').RateLimiter, writeLimiter = new RateLimiter(60, 'second'), readLimiter = new RateLimiter(100, 'second'), authLimiter = new RateLimiter(10, 5 * 60 * 1000), requestRaw = require('request').defaults({ json: false }), request = require('request').defaults({ json: true }); function buildHTTPServer(listener) { var app = http.createServer(listener), addr = app.address(), port, protocol; if (!addr) { app.listen(0); } port = app.address().port; protocol = app instanceof https.Server ? 'https' : 'http'; return protocol + '://127.0.0.1:' + port; } function endpoint(resource) { return ('function' === typeof resource) ? buildHTTPServer(resource) : resource; } function Appnexus(options) { options = options || {}; if (!options.endpoint) { throw new Error('"endpoint" can not be empty or null'); } options.endpoint = endpoint(options.endpoint); if (!options.username) { throw new Error('"user" can not be empty or null'); } if (!options.password) { throw new Error('"password" can not be empty or null'); } events.EventEmitter.call(this); this.endpoint = endpoint(options.endpoint); this.username = options.username; this.password = options.password; } util.inherits(Appnexus, events.EventEmitter); function sanitizeQueryOptions(options) { options.json = options.json || true; options.qs = options.qs || {}; for(var op in options){ if(op != 'json' && ~['boolean', 'string', 'number'].indexOf(typeof options[op])){ options.qs[op] = options[op]; } } } function eventForChange(type, method) { var methodToChangeMap = { 'POST': 'Created', 'PUT': 'Updated', 'DELETE': 'Deleted' }, typeOfChange = methodToChangeMap[method]; if (typeOfChange) { return type + typeOfChange; } return false; } Appnexus.prototype.query = function (type, options, callback) { var client = this; if (typeof options === 'function') { callback = options; options = null; } options = options || {}; sanitizeQueryOptions(options); options.headers = { 'Authorization': client.token }; options.url = options.url || client.endpoint + '/' + type; request(options, function (err, response, body) { if (debug.enabled) { if (this.body) { var buffer = new Buffer(this.body); var objectSent = JSON.stringify(JSON.parse(buffer.toString()), null, '\t'); debug("request %s %s sent: \n %s", this.method, this.href, objectSent); } } else { debug("request %s %s", this.method, this.href); } var ret; if (err) { return callback(err); } if (appnexusUtil.responseContainsError(response, body)) { return appnexusUtil.handleErrorResponse(body, client, callback); } ret = body; ret = body.response && body.response.id || ret; ret = body.response && body.response[type] || ret; callback(null, ret); if (eventForChange(type, options.method)) { client.emit(eventForChange(type, options.method), ret); } }); }; Appnexus.prototype.queryRaw = function (type, options, callback) { var client = this; if (typeof options === 'function') { callback = options; options = null; } options = options || {}; sanitizeQueryOptions(options); options.headers = { 'Authorization': client.token }; options.url = options.url || client.endpoint + '/' + type; requestRaw(options, function (err, response, body) { if (debug.enabled) { if (this.body) { var buffer = new Buffer(this.body); var objectSent = JSON.stringify(JSON.parse(buffer.toString()), null, '\t'); debug("request %s %s sent: \n %s", this.method, this.href, objectSent); } } else { debug("request %s %s", this.method, this.href); } var ret; if (err) { return callback(err); } ret = body; callback(null, ret); }); }; Appnexus.prototype.queryStream = function (type, options, callback) { var client = this; if (typeof options === 'function') { callback = options; options = null; } options = options || {}; sanitizeQueryOptions(options); options.headers = { 'Authorization': client.token, 'accept-encoding': 'gzip,deflate' }; options.url = options.url || client.endpoint + '/' + type; callback(null, requestRaw(options)); }; /** * HTTP VERBS */ function id2options(id, options) { console.log(id); console.log(options); if (!id) { return options; } options = options || {}; if (~['string', 'number'].indexOf(typeof id)) { options.id = id; return options; } return id; } function string2options(name, str, options) { if (!str) { return options; } options = options || {}; if (typeof str == 'string') { options[name] = str; return options; } return id; } Appnexus.prototype.get = function (type, id, options, callback) { var self = this; if (id instanceof Object && typeof options === 'function') { //no id. caller passed ("type", options, callback) callback = options; options = id; id = null; } else if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", id, callback) callback = options; options = null; } readLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(type, id2options(id, options), callback); }); }; Appnexus.prototype.search = function (type, term, options, callback) { var self = this; if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", "term", callback) callback = options; options = null; } readLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(type, string2options("search", term, options), callback); }); }; Appnexus.prototype.create = function (type, data, options, callback) { var json = {}, self = this; if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", data, callback) callback = options; options = null; } json[type] = data; options = options || {}; options.json = json; options.method = 'POST'; writeLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(type, options, callback); }); }; Appnexus.prototype.destroy = function (type, id, options, callback) { if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", id, callback) callback = options; options = null; } options = id2options(id, options); options.json = true; options.method = 'DELETE'; var self = this; writeLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(type, options, callback); }); }; Appnexus.prototype.update = function (type, id, data, options, callback) { var json = {}, options, self = this; if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", id, data, callback) callback = options; options = null; } json[type] = data; options = id2options(id, options); options.json = json; options.method = 'PUT'; // id can be a string or number, but some operations can require more // than one identifier, like modifying an existing advertiser segment // which requires both *code* and *member_id* writeLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(type, options, callback); }); }; Appnexus.prototype.authenticate = function (callback) { var url = this.endpoint + '/auth', payload = { auth: { username: this.username, password: this.password } }, that = this; authLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } request.post({ url: url, json: payload }, function (err, response, body) { var token; if (err) { return callback(err); } if (appnexusUtil.responseContainsError(response, body)) { return appnexusUtil.handleErrorResponse(body, that, callback); } token = body.response.token; if (callback) { callback(null, token); } that.emit('authenticationSucceed', token); }); }); }; Appnexus.prototype.getRaw = function (type, id, options, callback) { var self = this; if (typeof id === 'object' && typeof options === 'function') { //no id. caller passed ("type", options, callback) callback = options; options = id; id = null; } else if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", id, callback) callback = options; options = null; } readLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.queryRaw(type, id2options(id,options), callback); }); }; Appnexus.prototype.getStream = function (type, id, options, callback) { var self = this; if (typeof id === 'object' && typeof options === 'function') { //no id. caller passed ("type", options, callback) callback = options; options = id; id = null; } else if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", id, callback) callback = options; options = null; } readLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.queryStream(type, id2options(id,options), callback); }); }; Appnexus.prototype.custom = function (method, endpoint, data, options, callback) { var self = this; if (typeof callback === 'undefined' && typeof options === 'function') { //no options. caller passed ("type", data, callback) callback = options; options = null; } options = options || {}; options.json = data; options.method = method; writeLimiter.removeTokens(1, function (err, remainingRequests) { if (err) { this.emit('error', err); return; } self.query(endpoint, options, callback); }); }; function create(options) { return new Appnexus(options); } exports.create = create;