Find file
Fetching contributors…
Cannot retrieve contributors at this time
166 lines (126 sloc) 4.44 KB
querystring = require 'querystring'
https = require 'https'
sax = require 'sax'
_ = require 'underscore'
EvEApi request class
Always uses SSL to make requests. No support for EvEApi Proxy or other shit.
exports.EvEApi = class EveApi
server: api endpoint to contact
cacheProvider: A provider to in memory cache or another provider.
Must extend BaseCacheProvider.
constructor: (@server = '', @cacheProvider = new BaseCacheProvider) ->
if @server is ''
@server = ''
if @cacheProvider not instanceof BaseCacheProvider
throw new Error 'Providers must extend BaseCacheProvider!'
Make a api request.
Uses the configured @server and follows the configured ssl setting.
The callback HAS to be passed, otherwise you will not get any data.
If the call fails the callback will get called with an error object and null for data.
options: options needed for the api call. (api key and other parameters)
Must provide at least 'scope' and 'api'.
callback: callback to execute after the call completes
call: (options, callback) ->
if not callback?
throw new Error 'Must provide a callback!'
if not options
callback 'Must provide an options object!', null
if not options.scope? or not options.api?
callback 'Must provide scope and api in the options object!', null
# construct url
url = "/#{options.scope}/#{options.api}.xml.aspx"
# construct querystring
postData = querystring.stringify options
# check the cache
@cacheProvider.get url + postData, (err, data) =>
if data?
callback null, data
# no cache hit, execute a http/s request
options =
hostname: @server
port: 443
path: url
method: 'POST'
'Content-Type': 'application/x-www-form-urlencoded'
'Content-Length': postData.length
response = ""
req = https.request options, (res) =>
res.on 'data', (data) ->
response += data
res.on 'end', =>
@parseXML response, (error, obj) =>
cachedUntil = moment(obj.cacheduntil, 'YYYY-MM-DD HH:mm:ss').diff(moment(obj.currenttime, 'YYYY-MM-DD HH:mm:ss'), 'seconds')
@cacheProvider.set url + postData, obj, cachedUntil
callback null, obj
req.on 'error', (error) ->
callback error, null
req.write postData
Takes in an XML string and tries to parse it into a JSON object.
xmlString: the xml
parseXML: (xmlString, callback) ->
if not xmlString or xmlString is ''
callback 'XML cannot be empty!', null
parser = sax.parser false,
trim: true
normalize: true
lowercasetags: true
jsonObj = {}
currentNode = ""
nodeRef = jsonObj
parser.onerror = (err) ->
console.log err
callback err, null
parser.ontext = (text) ->
nodeRef[currentNode] = text
parser.onopentag = (node) ->
name =
# use the nodes own name if it exists
if node.attributes and
name =
# should not advance hierarchy on row nodes.
if isnt 'row'
currentNode = name
nodeRef[name] =
parent: nodeRef
nodeRef = nodeRef[name]
# special case for rowsets, rows go into arrays
if is 'rowset'
nodeRef['rows'] = []
index = nodeRef['rows'].push {parent: nodeRef}
nodeRef = nodeRef['rows'][index - 1]
# stupid saxjs, attributes object exists even if there are no attributes
if node.attributes and _.size(node.attributes) > 0
# loop through all attributes and add them as properties to the current node object
# if the current object is a row node, just construct a object of all attribs to add to the array.
for attribName, attribValue of node.attributes
nodeRef[attribName] = attribValue
parser.onclosetag = (node) ->
# concenate objects
if _.size(nodeRef) is 2 and nodeRef[node]
nodeRef.parent[node] = nodeRef[node]
# move hierarchy back one up
nodeRef = nodeRef.parent
parser.onend = () ->
callback null, nodeRef.eveapi
parser.write xmlString