I have a simple http client module(api.js) which is returning a promise as in the followings:
exports.endpoint = '';
exports.GET = function(args){
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.POST = function(args){
args.method = 'POST';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.PUT = function(args){
args.method = 'PUT';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.DELETE= function(args){
args.method = 'DELETE';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
var asyncApiCall = function(args){
var rp = require('request-promise');
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
}
return rp(options);
};
and I use the module like this:
var api = require('./api.js');
var args = {
uri : '/posts'
}
api.endpoint = 'http://localhost:3000';
api.GET(args)
.then(function(res){
console.log(res);
}, function(err){
console.log(err);
});
Now, I want to improve the module as much as possible. Is there any way to not repeat the export.functionName? I found the module.exports in NodeJS, but I am not sure how to use it in this case. How can I set the endpoint variable once in asyncApiCall function instead of all the other functions that return asyncApiCall?
Just another style:
var rp = require('request-promise'); // Put it here so you don't have to require 1 module so many times.
var asyncApiCall = function(args) {
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
};
return rp(options);
};
// Let's hack it.
var endpoint = '';
var apis = {};
['GET', 'POST', 'PUT', 'DELETE'].forEach(function(method) {
apis[method] = function(args) {
args.method = method;
args.uri = endpoint + args.uri;
return asyncApiCall(args);
}
});
module.exports = apis;
module.exports.endpoint = '';
A lot of people chose to put their export methods on a new object and export via module.exports, e.g.
var myExports = {
get: function () {},
post: function () {}
}
module.exports = myExports;
As for module.exports vs exports
It looks like it may be appropriate to set up a full constructor with your methods tied to it, like so:
var requests = function (endpoint) {
this.endpoint = endpoint;
}
requests.prototype.GET = function (args) {
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
}
// And so on
module.exports = requests;
And then call it like so:
var api = require('./api.js');
var endpoint = new api("http://localhost:3000");
endpoint.GET()
Wrap this in a class and export a new instance of it
function Module() {
}
Module.prototype.GET = function () {}
module.export = new Module()
// or
module.export = Module
// to call the constructor for your endpoint variable.
Related
This question already has answers here:
Return from a promise then()
(8 answers)
Closed 9 months ago.
I have this class written in javascript but I have difficulty in obtaining the result from the axios request, below my situation to better explain the problem:
i have a file called vtiger.js in the classes directory in the project root
vtiger.js
const axios = require('axios');
var md5 = require('md5');
var qs = require('qs');
const https = require('https');
class vTiger {
constructor() {
this.url = process.env.VTIGER_URL;
this.username = process.env.VTIGER_USERNAME;
this.password = process.env.VTIGER_PASSWORD;
}
async getchallengeTokenVtiger() {
var token;
var tokenmd5 = false;
var url = this.url + 'webservice.php?operation=getchallenge&username=' + this.username;
axios.get(url,
{
headers: {
"content-type": "application/x-www-form-urlencoded"
},
httpsAgent: new https.Agent(
{
rejectUnauthorized: false
})
}).then(response => {
if (response.data.success) {
token = response.data.result.token;
tokenmd5 = md5(token + this.password);
return tokenmd5;
}
});
}
}
module.exports = vTiger
then I have a file called api.js in the controllers folder with this content:
const http = require('http');
const axios = require('axios');
var qs = require('qs');
const vTiger = require('../classes/vtiger');
exports.welcome = (req, res, next) => {
const vtigerClass = new vTiger();
console.log(vtigerClass.getchallengeTokenVtiger())
res.status(200).json({
data: vtigerClass.getchallengeTokenVtiger()
});
}
from this file as an response I get:
{
"data": {}
}
while from the console.log(vtigerClass.getchallengeTokenVtiger()) line I get this:
Promise { undefined }
Where am I doing wrong?
Thanks
you probably don't want to use the .then in an async function. you should
const response = await axios.get(...)
if (response.data.success) {
token = response.data.result.token;
tokenmd5 = md5(token + this.password);
return tokenmd5;
}
else return null;
Or you can create a variable in your function called tokenmd5 i.e.
let tokenmd5 = ''
set its value in the .then, then return tokenmd5 at then end of your function NOT in the .then
THEN:
for your console.log you want:
exports.welcome = async (req, res, next) => {
const vtigerClass = new vTiger();
console.log(await vtigerClass.getchallengeTokenVtiger())
res.status(200).json({
data: vtigerClass.getchallengeTokenVtiger()
});
}
I need to assert whether a constructor was called using sinon. Below is how I can create a spy.
let nodeStub: any;
nodeStub = this.createStubInstance("node");
But how can I verify that this constructor was called with the relevant parameters? Below is how the constructor is actually called.
node = new node("test",2);
Any help would be much appreciated.
Below is the code I have.
import {Node} from 'node-sdk-js-browser';
export class MessageBroker {
private node: Node;
constructor(url: string, connectionParams: IConnectionParams) {
this.node = new Node(url, this.mqttOptions, this.messageReceivedCallBack);
}
}
Given the following code myClient.js:
const Foo = require('Foo');
module.exports = {
doIt: () => {
const f = new Foo("bar");
f.doSomething();
}
}
You can write a test like this:
const sandbox = sinon.sandbox.create();
const fooStub = {
doSomething: sandbox.spy(),
}
const constructorStub = sandbox.stub().returns(fooStub);
const FooInitializer = sandbox.stub({}, 'constructor').callsFake(constructorStub);
// I use proxyquire to mock dependencies. Substitute in whatever you use here.
const client2 = proxyquire('./myClient', {
'Foo': FooInitializer,
});
client2.doIt();
assert(constructorStub.calledWith("bar"));
assert(fooStub.doSomething.called);
//module.js
var Node = require('node-sdk-js-browser').Node;
function MessageBroker(url) {
this.node = new Node(url, 'foo', 'bar');
}
module.exports.MessageBroker = MessageBroker;
//module.test.js
var sinon = require('sinon');
var rewire = require('rewire');
var myModule = require('./module');
var MessageBroker = myModule.MessageBroker;
require('should');
require('should-sinon');
describe('module.test', function () {
describe('MessageBroker', function () {
it('constructor', function () {
var spy = sinon.spy();
var nodeSdkMock = {
Node: spy
};
var revert = myModule.__set__('node-sdk-js-browser', nodeSdkMock);
new MessageBroker('url');
spy.should.be.calledWith('url', 'foo', 'bar');
revert();
});
});
});
I have following module.
var Sendcloud = require('sendcloud');
var sc = new Sendcloud("key1","key2","key3");
var service = {};
service.restorePassword = function (params, cb) {
if (!params.to || !params.name || !params.token) {
throw "Miss params"
}
var defaultTemplate = adminBaseUrl + "reset/token/" + params.token;
var subject = params.subject || "Letter";
var template = params.template || defaultTemplate;
// Send email
sc.send(params.to, subject, template).then(function (info) {
console.log(info)
if (info.message === "success") {
return cb(null, "success");
} else {
return cb("failure", null);
}
});
};
module.exports = service;
I experience problems stubbing sc.send method. How to correctly cover this point using sinon.js? Or maybe I need to replace sendcloud module?
You need to use proxyquire module or rewire module.
Here an example of using proxyquire
var proxyquire = require('proxyquire');
var sinon = require('sinon');
var Sendcloud = require('sendcloud');
require('sinon-as-promised');
describe('service', function() {
var service;
var sc;
beforeEach(function() {
delete require.cache['sendcloud'];
sc = sinon.createStubInstance(Sendcloud);
service = proxyquire('./service', {
'sendcloud': sinon.stub().returns(sc)
});
});
it('#restorePassword', function(done) {
sc.send.resolves({});
var obj = {
to: 'to',
name: 'name',
token: 'token'
};
service.restorePassword(obj, function() {
console.log(sc.send.args[0]);
done();
});
});
});
I am trying to build a slack bot, and I have come across an error I can't make sense of.
This is the error:
/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:104
throw e;
^
Error: [Slack Bot Error] undefined
at assert (/Users/maecapozzi/Desktop/maebot/node_modules/slackbots/libs/utils.js:15:15)
at /Users/maecapozzi/Desktop/maebot/node_modules/slackbots/index.js:42:9
at Array.<anonymous> (/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:712:56)
at Immediate.callFns [as _onImmediate] (/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:23:35)
at tryOnImmediate (timers.js:534:15)
at processImmediate [as _immediateCallback] (timers.js:514:5)
I'll share my code with you as well, since that should help to make more sense of what has happened.
bot.js :
'use strict';
var MaeBot = require('../lib/maebot');
var token = process.env.BOT_API_KEY;
var dbPath = process.env.BOT_DB_PATH;
var name = process.env.BOT_NAME;
var maebot = new MaeBot({
token: token,
dbPath: dbPath,
name: name
});
maebot.run();
database.js:
var pg = require('pg');
var connectionString = process.env.DATABASE_URL || 'postgres://localhost:5432/maebot';
var client = new pg.Client(connectionString);
client.connect();
var query = client.query('CREATE TABLE dacts(id SERIAL PRIMARY KEY, text VARCHAR(40) not null)');
query.on('end', function() { client.end(); });
maebot.js:
'use strict';
var util = require('util');
var path = require('path');
var fs = require('fs');
var PostGres = require('postgresql');
var Bot = require('slackbots');
var MaeBot = function Constructor(settings) {
this.settings = settings;
this.settings.name = this.settings.name || 'maebot';
this.dbPath = settings.dbPath || path.resolve(process.cwd(), 'data', 'database.js');
this.user = null;
this.db = null;
};
MaeBot.prototype.run = function () {
MaeBot.super_.call(this, this.settings);
this.on('start', this._onStart);
this.on('message', this._onMessage);
};
MaeBot.prototype._onStart = function () {
this._loadBotUser();
this._connectDB();
};
MaeBot.prototype._loadBotUser = function () {
var self = this;
this.user = this.users.filter (function (user) {
return user.name === self.name;
})[0];
};
MaeBot.prototype._connectDB = function () {
if (!fs.existsSync(this.dbPath)) {
console.error('Database path ' + '"' + this.dbPath + '" does not exists or it\'s not readable."')
process.exit(1);
}
this.db = new PostGres.Database(this.dbPath);
};
MaeBot.prototype._welcomeMessage = function () {
this.postMessageToChannel(this.channels[0].name, 'Hi! Maebot here.' +
'\n I can tell you about my creator, Mae. Just say `Hi, maebot` or `' + this.name + '` to invoke me!',
{as_user: true});
};
MaeBot.prototype._onMessage = function (message) {
if (this._isChatMessage(message) &&
this._isChannelConversation(message) &&
!this._isFromMaeBot(message) &&
this._isMentioningMaeBot(message)
) {
this._replyWithRandomFact(message);
}
};
MaeBot.prototype._isChatMessage = function (message) {
return message.type === 'message' && Boolean(message.text);
};
MaeBot.prototype._isChannelConversation = function (message) {
return typeof message.channel === 'string' &&
message.channel[0] === 'C';
};
MaeBot.prototype._isFromMaeBot = function (message) {
return message.user === this.user.id;
};
MaeBot.prototype._isMentioningMaeBot = function (message) {
return message.text.toLowerCase().indexOf('maebot') > -1 ||
message.text.toLowerCase().indexOf(this.name) > -1;
};
MaeBot.prototype._replyWithRandomFact = function (originalMessage) {
var self = this;
self.db.get('SELECT id, fact FROM facts ORDER BY used ASC, RANDOM() LIMIT 1', function (err, record) {
if (err) {
return console.error('DATABASE ERROR:', err);
}
var channel = self._getChannelById(originalMessage.channel);
self.postMessageToChannel(channel.name, record.fact, {as_user: true});
self.db.run('UPDATE facts SET used = used + 1 WHERE id = ?', record.id);
});
};
MaeBot.prototype._getChannelById = function (channelId) {
return this.channels.filter(function (item) {
return item.id === channelId;
})[0];
};
util.inherits(MaeBot, Bot);
module.exports = MaeBot;
Your bot user and/or token is likely incorrect. Try re-creating your bot and ensure that you're using a valid token for your app.
in this code: if i try to pass this.handler as parameter to server.createServer() , then i get no response (the page keeps loading in browser). but if i use server.createServer(function(req, res) { //same code here as in handler() }) , then it works. what am i doing wrong?
var Con = module.exports = function() {
process.EventEmitter.call(this);
}
var createServer = module.exports.createServer = function(options) {
console.log('start');
this.port = options.port || 9122;
this.secure = options.secure || false;
if(this.secure === true)
if(!options.key || !options.certificate)
this.secure = false;
else {
this.key = options.key;
this.certificate = options.certificate;
}
if(this.secure) {
this.server = require('https');
var fs = require('fs');
var opt = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
this.server.createServer(opt, this.handler).listen(this.port);
} else {
this.server = require('http');
this.server.createServer(this.handler).listen(this.port);
}
}
Con.prototype.handler = function(req, res) {
console.log('request');
res.writeHead(200);
res.write(req.url);
res.end();
}
var Con = function() {
process.EventEmitter.call(this);
}
That's your constuctor
module.exports = new Con();
That's your instance
var createServer = module.exports.createServer = function(options) {
console.log('start');
this.port = options.port || 9122;
this.secure = options.secure || false;
if(this.secure === true)
if(!options.key || !options.certificate)
this.secure = false;
else {
this.key = options.key;
this.certificate = options.certificate;
}
if(this.secure) {
this.server = require('https');
var fs = require('fs');
var opt = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
this.server.createServer(opt, this.handler).listen(this.port);
} else {
this.server = require('http');
this.server.createServer(this.handler).listen(this.port);
}
}
.createServer is now a method on the instance rather then the constructor.
Since it's on the instance it also has access to the .handler method defined on the instance through the prototype.