@rokid/cloudapp-engine
Version:
Rokid's CloudApp Engine which implements CloudApp Protocol
142 lines (137 loc) • 4.12 kB
JavaScript
;
const path = require('path');
const https = require('https');
const crypto = require('crypto');
const querystring = require('querystring');
const protobuf = require('protobufjs');
const SendEventSrc = path.join(__dirname, './SendEvent.proto');
const SendEventProto = protobuf.loadSync(SendEventSrc);
const SendEventRequest = SendEventProto.lookupType('SendEventRequest');
const SendEventResponse = SendEventProto.lookupType('SendEventResponse');
/**
* @class SendEvent
*/
class SendEvent {
/**
* @method constructor
* @param {String} host
* @param {Object} config
* @param {String} config.key
* @param {String} config.device_type_id
* @param {String} config.device_id
* @param {String} config.secret
* @param {String} config.api_version
*/
constructor(host, config) {
this._pathname = '/v1/skill/dispatch/sendEvent'
this._host = host || 'apigwrest.open.rokid.com';
this._config = {
key: config.key,
device_type_id: config.device_type_id,
device_id: config.device_id || 'cloudapp-engine_v1',
service: 'rest',
version: config.api_version || '1',
secret: config.secret,
};
if (!this._config.key)
throw new TypeError('"key" is required for SendEvent');
if (!this._config.secret)
throw new TypeError('"secret" is required for SendEvent');
if (!this._config.device_type_id)
throw new TypeError('"device_type_id" is required for SendEvent');
}
/**
* @getter HTTP_OPTIONS
*/
get HTTP_OPTIONS() {
return {
method: 'POST',
host: this._host,
path: this._pathname,
headers: {
Authorization: this.HTTP_AUTHORIZATION,
}
};
}
/**
* @getter HTTP_AUTHORIZATION
*/
get HTTP_AUTHORIZATION() {
const data = {
key: this._config.key,
device_type_id: this._config.device_type_id,
device_id: this._config.device_id,
service: this._config.service,
version: this._config.version,
time: Math.floor(Date.now() / 1000),
secret: this._config.secret,
};
return [
`version=${data.version}`,
`time=${data.time}`,
`sign=${this.sign(data)}`,
`key=${data.key}`,
`device_type_id=${data.device_type_id}`,
`device_id=${data.device_id}`,
`service=${data.service}`,
].join(';');
}
/**
* @method sign
* @param {String} data - the string to sign
*/
sign(data) {
return crypto.createHash('md5')
.update(querystring.stringify(data))
.digest('hex')
.toUpperCase();
}
/**
* @method encodeRequest
* @param {String} appid - the appid
* @param {String} event - the event name
* @param {Object} extra - the extra to encode
* @return {Buffer} encoded buffer.
*/
encodeRequest(appid, event, extra) {
const pbdata = SendEventRequest.create({
appId: appid,
event,
extra: JSON.stringify(extra),
});
return SendEventRequest.encode(pbdata).finish();
}
/**
* @method send
* @param {String} appid - the appid
* @param {String} event - the event name
* @param {Object} options - the options to send
* @param {Function} callback
*/
send(appid, event, options, callback) {
if (typeof callback !== 'function')
throw new Error('callback must be a function');
const data = this.encodeRequest(appid, event, options);
const req = https.request(this.HTTP_OPTIONS, (res) => {
var list = [];
res.on('data', (chunk) => list.push(chunk));
res.on('end', () => {
const databuf = Buffer.concat(list);
if (res.statusCode !== 200) {
const err = new Error(`failed upload ${event} with ${databuf.toString('utf8')}`);
callback(err);
} else {
const msg = SendEventResponse.decode(databuf);
// console.log(`got ${event} successfully response`, msg.response);
callback(null, JSON.parse(msg.response));
}
});
});
req.on('error', (err) => {
console.error(err && err.stack);
});
req.write(data);
req.end();
}
}
module.exports = SendEvent;