converted rocketchat-integrations coffee to js (#6502)

* converted rocketchat-integrations coffee to js

* fix

* fix lint and suggestions
pull/6542/head
Guilherme Gazzo 9 years ago committed by Rodrigo Nascimento
parent da0fe37298
commit bc5878a42a
  1. 3
      packages/rocketchat-integrations/package.js
  2. 294
      packages/rocketchat-integrations/server/api/api.coffee
  3. 332
      packages/rocketchat-integrations/server/api/api.js

@ -8,7 +8,6 @@ Package.describe({
Package.onUse(function(api) {
api.use('mongo');
api.use('coffeescript');
api.use('underscore');
api.use('ecmascript');
api.use('babel-compiler');
@ -65,7 +64,7 @@ Package.onUse(function(api) {
api.addFiles('server/methods/clearIntegrationHistory.js', 'server');
// api
api.addFiles('server/api/api.coffee', 'server');
api.addFiles('server/api/api.js', 'server');
api.addFiles('server/lib/triggerHandler.js', 'server');
api.addFiles('server/triggers.js', 'server');

@ -1,294 +0,0 @@
vm = Npm.require('vm')
moment = require('moment')
compiledScripts = {}
buildSandbox = (store = {}) ->
sandbox =
_: _
s: s
console: console,
moment: moment,
Store:
set: (key, val) ->
return store[key] = val
get: (key) ->
return store[key]
HTTP: (method, url, options) ->
try
return {} =
result: HTTP.call method, url, options
catch e
return {} =
error: e
Object.keys(RocketChat.models).filter((k) ->
return !k.startsWith('_')
).forEach (k) =>
sandbox[k] = RocketChat.models[k]
return {} =
store: store,
sandbox: sandbox
getIntegrationScript = (integration) ->
compiledScript = compiledScripts[integration._id]
if compiledScript? and +compiledScript._updatedAt is +integration._updatedAt
return compiledScript.script
script = integration.scriptCompiled
vmScript = undefined
sandboxItems = buildSandbox()
try
logger.incoming.info 'Will evaluate script of Trigger', integration.name
logger.incoming.debug script
vmScript = vm.createScript script, 'script.js'
vmScript.runInNewContext sandboxItems.sandbox
if sandboxItems.sandbox.Script?
compiledScripts[integration._id] =
script: new sandboxItems.sandbox.Script()
store: sandboxItems.store
_updatedAt: integration._updatedAt
return compiledScripts[integration._id].script
catch e
logger.incoming.error '[Error evaluating Script in Trigger', integration.name, ':]'
logger.incoming.error script.replace(/^/gm, ' ')
logger.incoming.error "[Stack:]"
logger.incoming.error e.stack.replace(/^/gm, ' ')
throw RocketChat.API.v1.failure 'error-evaluating-script'
if not sandboxItems.sandbox.Script?
logger.incoming.error '[Class "Script" not in Trigger', integration.name, ']'
throw RocketChat.API.v1.failure 'class-script-not-found'
Api = new Restivus
enableCors: true
apiPath: 'hooks/'
auth:
user: ->
payloadKeys = Object.keys @bodyParams
payloadIsWrapped = @bodyParams?.payload? and payloadKeys.length == 1
if payloadIsWrapped and @request.headers['content-type'] is 'application/x-www-form-urlencoded'
try
@bodyParams = JSON.parse @bodyParams.payload
catch e
return {
error: {
statusCode: 400
body: {
success: false
error: e.message
}
}
}
@integration = RocketChat.models.Integrations.findOne
_id: @request.params.integrationId
token: decodeURIComponent @request.params.token
if not @integration?
logger.incoming.info "Invalid integration id", @request.params.integrationId, "or token", @request.params.token
return
user = RocketChat.models.Users.findOne
_id: @integration.userId
return user: user
createIntegration = (options, user) ->
logger.incoming.info 'Add integration', options.name
logger.incoming.debug options
Meteor.runAsUser user._id, =>
switch options['event']
when 'newMessageOnChannel'
options.data ?= {}
if options.data.channel_name? and options.data.channel_name.indexOf('#') is -1
options.data.channel_name = '#' + options.data.channel_name
Meteor.call 'addOutgoingIntegration',
username: 'rocket.cat'
urls: [options.target_url]
name: options.name
channel: options.data.channel_name
triggerWords: options.data.trigger_words
when 'newMessageToUser'
if options.data.username.indexOf('@') is -1
options.data.username = '@' + options.data.username
Meteor.call 'addOutgoingIntegration',
username: 'rocket.cat'
urls: [options.target_url]
name: options.name
channel: options.data.username
triggerWords: options.data.trigger_words
return RocketChat.API.v1.success()
removeIntegration = (options, user) ->
logger.incoming.info 'Remove integration'
logger.incoming.debug options
integrationToRemove = RocketChat.models.Integrations.findOne urls: options.target_url
Meteor.runAsUser user._id, =>
Meteor.call 'deleteOutgoingIntegration', integrationToRemove._id
return RocketChat.API.v1.success()
executeIntegrationRest = ->
logger.incoming.info 'Post integration:', @integration.name
logger.incoming.debug '@urlParams:', @urlParams
logger.incoming.debug '@bodyParams:', @bodyParams
if @integration.enabled isnt true
return {} =
statusCode: 503
body: 'Service Unavailable'
defaultValues =
channel: @integration.channel
alias: @integration.alias
avatar: @integration.avatar
emoji: @integration.emoji
if @integration.scriptEnabled is true and @integration.scriptCompiled? and @integration.scriptCompiled.trim() isnt ''
script = undefined
try
script = getIntegrationScript(@integration)
catch e
logger.incoming.warn e
return RocketChat.API.v1.failure e.message
request =
url:
hash: @request._parsedUrl.hash
search: @request._parsedUrl.search
query: @queryParams
pathname: @request._parsedUrl.pathname
path: @request._parsedUrl.path
url_raw: @request.url
url_params: @urlParams
content: @bodyParams
content_raw: @request._readableState?.buffer?.toString()
headers: @request.headers
user:
_id: @user._id
name: @user.name
username: @user.username
try
sandboxItems = buildSandbox(compiledScripts[@integration._id].store)
sandbox = sandboxItems.sandbox
sandbox.script = script
sandbox.request = request
result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, { timeout: 3000 })
if result?.error?
return RocketChat.API.v1.failure result.error
@bodyParams = result?.content
logger.incoming.debug '[Process Incoming Request result of Trigger', @integration.name, ':]'
logger.incoming.debug 'result', @bodyParams
catch e
logger.incoming.error '[Error running Script in Trigger', @integration.name, ':]'
logger.incoming.error @integration.scriptCompiled.replace(/^/gm, ' ')
logger.incoming.error "[Stack:]"
logger.incoming.error e.stack.replace(/^/gm, ' ')
return RocketChat.API.v1.failure 'error-running-script'
if not @bodyParams?
return RocketChat.API.v1.failure 'body-empty'
@bodyParams.bot =
i: @integration._id
try
message = processWebhookMessage @bodyParams, @user, defaultValues
if _.isEmpty message
return RocketChat.API.v1.failure 'unknown-error'
return RocketChat.API.v1.success()
catch e
return RocketChat.API.v1.failure e.error
addIntegrationRest = ->
return createIntegration @bodyParams, @user
removeIntegrationRest = ->
return removeIntegration @bodyParams, @user
integrationSampleRest = ->
logger.incoming.info 'Sample Integration'
return {} =
statusCode: 200
body: [
token: Random.id(24)
channel_id: Random.id()
channel_name: 'general'
timestamp: new Date
user_id: Random.id()
user_name: 'rocket.cat'
text: 'Sample text 1'
trigger_word: 'Sample'
,
token: Random.id(24)
channel_id: Random.id()
channel_name: 'general'
timestamp: new Date
user_id: Random.id()
user_name: 'rocket.cat'
text: 'Sample text 2'
trigger_word: 'Sample'
,
token: Random.id(24)
channel_id: Random.id()
channel_name: 'general'
timestamp: new Date
user_id: Random.id()
user_name: 'rocket.cat'
text: 'Sample text 3'
trigger_word: 'Sample'
]
integrationInfoRest = ->
logger.incoming.info 'Info integration'
return {} =
statusCode: 200
body:
success: true
Api.addRoute ':integrationId/:userId/:token', authRequired: true, {post: executeIntegrationRest, get: executeIntegrationRest}
Api.addRoute ':integrationId/:token', authRequired: true, {post: executeIntegrationRest, get: executeIntegrationRest}
Api.addRoute 'sample/:integrationId/:userId/:token', authRequired: true, {get: integrationSampleRest}
Api.addRoute 'sample/:integrationId/:token', authRequired: true, {get: integrationSampleRest}
Api.addRoute 'info/:integrationId/:userId/:token', authRequired: true, {get: integrationInfoRest}
Api.addRoute 'info/:integrationId/:token', authRequired: true, {get: integrationInfoRest}
Api.addRoute 'add/:integrationId/:userId/:token', authRequired: true, {post: addIntegrationRest}
Api.addRoute 'add/:integrationId/:token', authRequired: true, {post: addIntegrationRest}
Api.addRoute 'remove/:integrationId/:userId/:token', authRequired: true, {post: removeIntegrationRest}
Api.addRoute 'remove/:integrationId/:token', authRequired: true, {post: removeIntegrationRest}

@ -0,0 +1,332 @@
/* globals Api Meteor Restivus logger processWebhookMessage*/
// TODO: remove globals
import vm from 'vm';
import moment from 'moment';
const compiledScripts = {};
function buildSandbox(store = {}) {
const sandbox = {
_,
s,
console,
moment,
Store: {
set(key, val) {
return store[key] = val;
},
get(key) {
return store[key];
}
},
HTTP(method, url, options) {
try {
return {
result: HTTP.call(method, url, options)
};
} catch (error) {
return {
error
};
}
}
};
Object.keys(RocketChat.models).filter((k) => !k.startsWith('_')).forEach((k) => sandbox[k] = RocketChat.models[k]);
return { store, sandbox };
}
function getIntegrationScript(integration) {
const compiledScript = compiledScripts[integration._id];
if ((compiledScript != null) && +compiledScript._updatedAt === +integration._updatedAt) {
return compiledScript.script;
}
const script = integration.scriptCompiled;
const {sandbox, store} = buildSandbox();
try {
logger.incoming.info('Will evaluate script of Trigger', integration.name);
logger.incoming.debug(script);
const vmScript = vm.createScript(script, 'script.js');
vmScript.runInNewContext(sandbox);
if (sandbox.Script != null) {
compiledScripts[integration._id] = {
script: new sandbox.Script(),
store,
_updatedAt: integration._updatedAt
};
return compiledScripts[integration._id].script;
}
} catch ({stack}) {
logger.incoming.error('[Error evaluating Script in Trigger', integration.name, ':]');
logger.incoming.error(script.replace(/^/gm, ' '));
logger.incoming.error('[Stack:]');
logger.incoming.error(stack.replace(/^/gm, ' '));
throw RocketChat.API.v1.failure('error-evaluating-script');
}
if (sandbox.Script == null) {
logger.incoming.error('[Class "Script" not in Trigger', integration.name, ']');
throw RocketChat.API.v1.failure('class-script-not-found');
}
}
Api = new Restivus({
enableCors: true,
apiPath: 'hooks/',
auth: {
user() {
const payloadKeys = Object.keys(this.bodyParams);
const payloadIsWrapped = (this.bodyParams && this.bodyParams.payload) && payloadKeys.length === 1;
if (payloadIsWrapped && this.request.headers['content-type'] === 'application/x-www-form-urlencoded') {
try {
this.bodyParams = JSON.parse(this.bodyParams.payload);
} catch ({message}) {
return {
error: {
statusCode: 400,
body: {
success: false,
error: message
}
}
};
}
}
this.integration = RocketChat.models.Integrations.findOne({
_id: this.request.params.integrationId,
token: decodeURIComponent(this.request.params.token)
});
if (this.integration == null) {
logger.incoming.info('Invalid integration id', this.request.params.integrationId, 'or token', this.request.params.token);
return;
}
const user = RocketChat.models.Users.findOne({
_id: this.integration.userId
});
return {user};
}
}
});
function createIntegration(options, user) {
logger.incoming.info('Add integration', options.name);
logger.incoming.debug(options);
Meteor.runAsUser(user._id, function() {
switch (options['event']) {
case 'newMessageOnChannel':
if (options.data == null) {
options.data = {};
}
if ((options.data.channel_name != null) && options.data.channel_name.indexOf('#') === -1) {
options.data.channel_name = `#${ options.data.channel_name }`;
}
return Meteor.call('addOutgoingIntegration', {
username: 'rocket.cat',
urls: [options.target_url],
name: options.name,
channel: options.data.channel_name,
triggerWords: options.data.trigger_words
});
case 'newMessageToUser':
if (options.data.username.indexOf('@') === -1) {
options.data.username = `@${ options.data.username }`;
}
return Meteor.call('addOutgoingIntegration', {
username: 'rocket.cat',
urls: [options.target_url],
name: options.name,
channel: options.data.username,
triggerWords: options.data.trigger_words
});
}
});
return RocketChat.API.v1.success();
}
function removeIntegration(options, user) {
logger.incoming.info('Remove integration');
logger.incoming.debug(options);
const integrationToRemove = RocketChat.models.Integrations.findOne({
urls: options.target_url
});
Meteor.runAsUser(user._id, () => {
return Meteor.call('deleteOutgoingIntegration', integrationToRemove._id);
});
return RocketChat.API.v1.success();
}
function executeIntegrationRest() {
logger.incoming.info('Post integration:', this.integration.name);
logger.incoming.debug('@urlParams:', this.urlParams);
logger.incoming.debug('@bodyParams:', this.bodyParams);
if (this.integration.enabled !== true) {
return {
statusCode: 503,
body: 'Service Unavailable'
};
}
const defaultValues = {
channel: this.integration.channel,
alias: this.integration.alias,
avatar: this.integration.avatar,
emoji: this.integration.emoji
};
if (this.integration.scriptEnabled === true && this.integration.scriptCompiled && this.integration.scriptCompiled.trim() !== '') {
let script;
try {
script = getIntegrationScript(this.integration);
} catch (e) {
logger.incoming.warn(e);
return RocketChat.API.v1.failure(e.message);
}
const request = {
url: {
hash: this.request._parsedUrl.hash,
search: this.request._parsedUrl.search,
query: this.queryParams,
pathname: this.request._parsedUrl.pathname,
path: this.request._parsedUrl.path
},
url_raw: this.request.url,
url_params: this.urlParams,
content: this.bodyParams,
content_raw: this.request._readableState && this.request._readableState.buffer && this.request._readableState.buffer.toString(),
headers: this.request.headers,
user: {
_id: this.user._id,
name: this.user.name,
username: this.user.username
}
};
try {
const {sandbox} = buildSandbox(compiledScripts[this.integration._id].store);
sandbox.script = script;
sandbox.request = request;
const result = vm.runInNewContext('script.process_incoming_request({ request: request })', sandbox, {
timeout: 3000
});
if (result && result.error) {
return RocketChat.API.v1.failure(result.error);
}
this.bodyParams = result && result.content;
logger.incoming.debug('[Process Incoming Request result of Trigger', this.integration.name, ':]');
logger.incoming.debug('result', this.bodyParams);
} catch ({stack}) {
logger.incoming.error('[Error running Script in Trigger', this.integration.name, ':]');
logger.incoming.error(this.integration.scriptCompiled.replace(/^/gm, ' '));
logger.incoming.error('[Stack:]');
logger.incoming.error(stack.replace(/^/gm, ' '));
return RocketChat.API.v1.failure('error-running-script');
}
}
if (this.bodyParams == null) {
return RocketChat.API.v1.failure('body-empty');
}
this.bodyParams.bot = {
i: this.integration._id
};
try {
const message = processWebhookMessage(this.bodyParams, this.user, defaultValues);
if (_.isEmpty(message)) {
return RocketChat.API.v1.failure('unknown-error');
}
return RocketChat.API.v1.success();
} catch ({error}) {
return RocketChat.API.v1.failure(error);
}
}
function addIntegrationRest() {
return createIntegration(this.bodyParams, this.user);
}
function removeIntegrationRest() {
return removeIntegration(this.bodyParams, this.user);
}
function integrationSampleRest() {
logger.incoming.info('Sample Integration');
return {
statusCode: 200,
body: [
{
token: Random.id(24),
channel_id: Random.id(),
channel_name: 'general',
timestamp: new Date,
user_id: Random.id(),
user_name: 'rocket.cat',
text: 'Sample text 1',
trigger_word: 'Sample'
}, {
token: Random.id(24),
channel_id: Random.id(),
channel_name: 'general',
timestamp: new Date,
user_id: Random.id(),
user_name: 'rocket.cat',
text: 'Sample text 2',
trigger_word: 'Sample'
}, {
token: Random.id(24),
channel_id: Random.id(),
channel_name: 'general',
timestamp: new Date,
user_id: Random.id(),
user_name: 'rocket.cat',
text: 'Sample text 3',
trigger_word: 'Sample'
}
]
};
}
function integrationInfoRest() {
logger.incoming.info('Info integration');
return {
statusCode: 200,
body: {
success: true
}
};
}
Api.addRoute(':integrationId/:userId/:token', { authRequired: true }, {
post: executeIntegrationRest,
get: executeIntegrationRest
});
Api.addRoute(':integrationId/:token', { authRequired: true }, {
post: executeIntegrationRest,
get: executeIntegrationRest
});
Api.addRoute('sample/:integrationId/:userId/:token', { authRequired: true }, {
get: integrationSampleRest
});
Api.addRoute('sample/:integrationId/:token', { authRequired: true }, {
get: integrationSampleRest
});
Api.addRoute('info/:integrationId/:userId/:token', { authRequired: true }, {
get: integrationInfoRest
});
Api.addRoute('info/:integrationId/:token', { authRequired: true }, {
get: integrationInfoRest
});
Api.addRoute('add/:integrationId/:userId/:token', { authRequired: true }, {
post: addIntegrationRest
});
Api.addRoute('add/:integrationId/:token', { authRequired: true }, {
post: addIntegrationRest
});
Api.addRoute('remove/:integrationId/:userId/:token', { authRequired: true }, {
post: removeIntegrationRest
});
Api.addRoute('remove/:integrationId/:token', { authRequired: true }, {
post: removeIntegrationRest
});
Loading…
Cancel
Save