UNPKG

meshblu-http

Version:
302 lines (231 loc) 11.9 kB
_ = require 'lodash' debug = require('debug')('meshblu-http') stableStringify = require 'json-stable-stringify' class MeshbluHttp constructor: (options={}, @dependencies={}) -> options = _.defaults(_.cloneDeep(options), port: 443, server: 'meshblu.octoblu.com') {@uuid, @token, @server, @port, @protocol, @auth, @raw} = options @protocol = null if @protocol == 'websocket' try @port = parseInt @port catch e @protocol ?= 'http' @protocol = 'https' if @port == 443 @urlBase = "#{@protocol}://#{@server}:#{@port}" @request = @dependencies.request ? require 'request' @NodeRSA = @dependencies.NodeRSA ? require 'node-rsa' getDefaultRequestOptions: => _.extend json: true, @getAuthRequestOptions() getRawRequestOptions: => headers = 'content-type' : 'application/json' _.extend json: false, headers: headers, @getAuthRequestOptions() getAuthRequestOptions: => return auth: @auth if @auth? return {} unless @uuid && @token auth: user: @uuid pass: @token createSubscription: (options, callback) => url = @_subscriptionUrl options requestOptions = @getDefaultRequestOptions() @request.post url, requestOptions, (error, response, body) => return callback @_userError(response.statusCode, body?.error ? 'Unknown Error Occurred') if response.statusCode != 204 callback() deleteSubscription: (options, callback) => url = @_subscriptionUrl options requestOptions = @getDefaultRequestOptions() @request.delete url, requestOptions, (error, response, body) => return callback @_userError(response.statusCode, body?.error ? 'Unknown Error Occurred') if response.statusCode != 204 callback() _subscriptionUrl: (options) => {subscriberUuid, emitterUuid, type} = options "#{@urlBase}/v2/devices/#{subscriberUuid}/subscriptions/#{emitterUuid}/#{type}" device: (deviceUuid, callback=->) => options = @getDefaultRequestOptions() @request.get "#{@urlBase}/v2/devices/#{deviceUuid}", options, (error, response, body) => debug "device", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode != 200 callback null, body search: (query, metadata, callback) => options = @getDefaultRequestOptions() options.headers = _.extend {}, @_getMetadataHeaders(metadata), options.headers options.json = query @request.post "#{@urlBase}/search/devices", options, (error, response, body) => return callback @_userError(500, error.message) if error? return callback null, body devices: (query={}, rest...) => [callback] = rest [metadata, callback] = rest if _.isPlainObject callback metadata ?= {} @_devices query, metadata, callback _devices: (query, metadata, callback=->) => options = @getDefaultRequestOptions() options.qs = query options.headers = _.extend {}, @_getMetadataHeaders(metadata), options.headers @request.get "#{@urlBase}/devices", options, (error, response, body) => debug "devices", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) if body?.error? callback null, body mydevices: (query={}, callback=->) => options = @getDefaultRequestOptions() options.qs = query @request.get "#{@urlBase}/mydevices", options, (error, response, body) => debug "mydevices", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) if body?.error? return callback @_userError(response.statusCode, body) if response.statusCode >= 400 callback null, body generateAndStoreToken: (deviceUuid, callback=->) => options = @getDefaultRequestOptions() @request.post "#{@urlBase}/devices/#{deviceUuid}/tokens", options, (error, response, body) => debug "generateAndStoreToken", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body) if response.statusCode >= 400 callback null, body generateAndStoreTokenWithOptions: (deviceUuid, tokenOptions, callback=->) => options = @getDefaultRequestOptions() options.json = tokenOptions if tokenOptions? @request.post "#{@urlBase}/devices/#{deviceUuid}/tokens", options, (error, response, body) => debug "generateAndStoreToken", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body) if response.statusCode >= 400 callback null, body generateKeyPair: => key = new @NodeRSA() key.generateKeyPair() privateKey: key.exportKey('private'), publicKey: key.exportKey('public') message: (message, rest...) => [callback] = rest [metadata, callback] = rest if _.isPlainObject callback metadata ?= {} @_message message, metadata, callback _message: (message, metadata, callback=->) => if @raw options = @getRawRequestOptions() options.body = message else options = @getDefaultRequestOptions() options.json = message options.headers = _.extend {}, @_getMetadataHeaders(metadata), options.headers debug 'POST', "#{@urlBase}/messages", options @request.post "#{@urlBase}/messages", options, (error, response, body) => debug "message", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) if body?.error? callback null, body _getMetadataHeaders: (metadata) => _.transform metadata, (newMetadata, value, key) => newMetadata["x-meshblu-#{key}"] = @_possiblySerializeHeaderValue value return true #because request doesn't serialize arrays correctly for headers. _possiblySerializeHeaderValue: (value) => return value if _.isString value return value if _.isBoolean value return value if _.isNumber value return JSON.stringify value publicKey: (deviceUuid, callback=->) => options = @getDefaultRequestOptions() @request.get "#{@urlBase}/devices/#{deviceUuid}/publickey", options, (error, response, body) => debug "publicKey", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response?.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode != 200 callback null, body register: (device, callback=->) => options = @getDefaultRequestOptions() options.json = device @request.post "#{@urlBase}/devices", options, (error, response, body={}) => debug "register", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode >= 400 callback null, body resetToken: (deviceUuid, callback=->) => options = @getDefaultRequestOptions() url = "#{@urlBase}/devices/#{deviceUuid}/token" @request.post url, options, (error, response, body) => return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) unless response.statusCode == 201 callback null, body revokeToken: (deviceUuid, deviceToken, callback=->) => options = @getDefaultRequestOptions() @request.del "#{@urlBase}/devices/#{deviceUuid}/tokens/#{deviceToken}", options, (error, response, body={}) => debug "revokeToken", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode >= 400 callback null revokeTokenByQuery: (deviceUuid, query, callback=->) => options = @getDefaultRequestOptions() options.qs = query @request.del "#{@urlBase}/devices/#{deviceUuid}/tokens", options, (error, response, body={}) => debug "revokeToken", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode >= 400 callback null setPrivateKey: (privateKey) => @privateKey = new @NodeRSA privateKey sign: (data) => @privateKey.sign(stableStringify(data)).toString('base64') unregister: (device, callback=->) => options = @getDefaultRequestOptions() @request.del "#{@urlBase}/devices/#{device.uuid}", options, (error, response, body) => debug "unregister", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode >= 400 callback null update: (uuid, params, rest...) => [callback] = rest [metadata, callback] = rest if _.isPlainObject callback metadata ?= {} @_update uuid, params, metadata, callback _update: (uuid, params, metadata, callback=->) => options = @getDefaultRequestOptions() options.json = params options.headers = _.extend {}, @_getMetadataHeaders(metadata), options.headers @request.patch "#{@urlBase}/v2/devices/#{uuid}", options, (error, response, body) => debug "update", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) unless response.statusCode == 204 callback null, body updateDangerously: (uuid, params, callback=->) => options = @getDefaultRequestOptions() options.json = params @request.put "#{@urlBase}/v2/devices/#{uuid}", options, (error, response, body) => debug "update", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body?.error) unless response.statusCode == 204 callback null, body verify: (message, signature) => @privateKey.verify stableStringify(message), signature, 'utf8', 'base64' whoami: (callback=->) => options = @getDefaultRequestOptions() @request.get "#{@urlBase}/v2/whoami", options, (error, response, body) => debug "whoami", error, body return callback @_userError(500, error.message) if error? return callback @_userError(response.statusCode, body.error.message) if body?.error? return callback @_userError(response.statusCode, body?.message || body) if response.statusCode != 200 callback null, body createHook: (uuid, type, url, callback) => allowedTypes = ['broadcast', 'sent', 'received', 'config'] return callback new Error "Hook type not supported. supported types are: #{allowedTypes.join ', '}" unless type in allowedTypes updateRequest = $addToSet: "meshblu.forwarders.#{type}": type: 'webhook' url: url method: 'POST', generateAndForwardMeshbluCredentials: true @updateDangerously(uuid, updateRequest, callback) _userError: (code, message) => error = new Error message error.code = code error module.exports = MeshbluHttp