Importing function from another module in node js - javascript

Importing certain function from some folder which has needed module with this function doesn't work.
I'm using nodemailer for sending emails. I have 3 different folders with modules. The problem is in importing (require) email sending function to current module from another one. It becomes undefined and error is myFunc is not a function.
I'm doing pretty simple things like requiring function from folder with index.js which includes needed function. But it becomes undefined when I try to use it.
services/mailTransport.js
const nodemailer = require('nodemailer');
const mailTransporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: 'test#test.com',
pass: 'myPassword'
}
});
module.exports = mailTransporter;
services/index.js
const mailTransporter = require("./mailTransporter");
module.exports = { mailTransporter }
utils/mailTemplate.js
const { mailTransporter } = require("../services");
const sendEmail = function (obj, msg) {
return new Promise( (res, rej) => {
let mailOptions = {
from: 'test#test.com',
to: `${obj.to}`,
subject: `${obj.subject}`,
text: "plain text",
html: "<b>" + msg + "</b>"
};
mailTransporter.sendMail(mailOptions, (error, info) => {
mailTransporter.close();
if (error) {
rej(error);
}
console.log('Message sent: %s', info.messageId);
res(info.messageId);
});
})
}
module.exports = { sendEmail };
And finally I want to use it here in projects/emails.js
const { sendEmail } = require("../utils/mailTemplate");
const { vendorNotificationMessage } = require("../utils/emailMessages");
async function notifyVendors(steps) {
try {
for(let step of steps) {
if(step.vendor) {
const message = vendorNotificationMessage(step);
step.to = step.vendor.email;
step.subject = "Step cancelling notification!";
await sendEmail(step, message);
}
}
} catch(err) {
console.log(err);
console.log("Error in notifyVendors");
}
}
module.exports = { notifyVendors };
I expect that email will be sent using that sendEmail function. But it stops with the error TypeError: sendEmail is not a function.

The correct syntax for exporting something from a module is
exports.globalObjectName = localObjectName
So in your first file the export statement should look like this
exports.mailTransporter = mailTransporter

I do not think that you need {} when you use module.exports and require. Try module.exports = sendEmail ; and const sendEmail = require("../utils/mailTemplate");

Related

How to stub a dependency in express middleware (express-validator/mocha/chai/sinon/proxyquire)

**Edit: Re-written with a simple example that works first:
So I've got a test file and 2 modules.
moduleA has a dependency, moduleB
// moduleA.js
const ModuleB = require('./moduleB');
function functionA() {
return 20 + ModuleB.functionB();
};
module.exports = { functionA };
// moduleB.js
const functionB = () => {
return 10;
}
module.exports = { functionB }
My test file stubs out functionB (returned from moduleB) using proxyquire:
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('Unit Tests', function() {
it('should work', () => {
const mockedFn = sinon.stub();
mockedFn.returns(30);
const copyModuleA = proxyquire('./moduleA', {
'./moduleB': {
functionB: mockedFn
}
});
console.log(copyModuleA.functionA());
})
});
So it outputs 50 (stubbed functionB 30 + functionA 20)
Now I'm trying to take this example into my code:
moduleA in this case is a file called validation.js. It is dependent on moduleB, in this case a sequelize model, Person, with the function I want to mock: findOne
validation.js exports module.exports = { validateLogin };, a function that calls validate, which returns a function that uses Person.findOne()
So in my mind, as with the simple example, I need to create a stub, point to the validation module in proxyquire, and reference the dependency and its findOne function. Like this:
const stubbedFindOne = sinon.stub();
stubbedFindOne.resolves();
validationModule = proxyquire('../../utils/validation', {
'../models/Person': {
findOne: stubbedFindOne
}
});
This should stub Person.findOne in validation.js. But it doesn't seem to. And I have no idea why.
let validationModule;
describe('Unit Tests', () => {
before(() => {
const stubbedFindOne = sinon.stub();
stubbedFindOne.resolves();
validationModule = proxyquire('../../utils/validation', {
'../models/Person': {
findOne: stubbedFindOne
}
});
})
it.only('should return 422 if custom email validation fails', async() => {
const wrongEmailReq = { body: {email: 'nik#hotmail.com'} };
const res = {
statusCode: 500,
status: (code) => {this.statusCode = code; return this},
};
const validationFn = validationModule.validateLogin();
const wrongEmail = await validationFn(wrongEmailReq, res, ()=>{});
expect(wrongEmail.errors[0].msg).to.be.equal('Custom Authorisation Error');
return;
})
And this is my validation.js file:
const Person = require('../models/Person');
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
const error = new Error();
error.message = process.env.NODE_ENV === 'development'? 'Validation Failed':'Error';
error.statusCode = !errors.isEmpty()? 422:500;
error.errors = errors.array({onlyFirstError: true});
next(error);
return error;
};
};
const validateLogin = () => {
const validations = [
body('email')
.isString()
// snip
.custom(async (value, {req}) => {
try{
const person = await Person.findOne({ where: { email: value } });
if(!person) return Promise.reject('Custom Authorisation Error');
} catch(err) {
throw err;
}
})
.trim(),
];
return validate(validations);
}
module.exports = {
validateLogin
};
So the code in both the small sample and my app is correct, apart from how I stub the function. It shouldn't resolve or reject anything (I tried both out of desperation). It should return null in order to satisfy the conditional rather than jump to the catch block:
try{
const person = await Person.findOne({ where: { email: value } });
if(!person) return Promise.reject('Custom Authorisation Error');
} catch(err) {
throw err;
}
Hope the simple example helps someone else with proxyquire though

Extracting and testing an express-validator function

I'd like to test my implementation of express-validator rules in my middleware. I understand that I shouldn't test 3rd party code, but in my (perhaps flawed) view, I'm testing my implementation rather than their code.
A cut down version would usually look like this:
// routes.js
router.post('/example',
[
body('email')
.isString()
.withMessage('Invalid characters, please use letters and numbers only'))
],
//// To be replaced by:
// validateLogin(),
controller.exampleFn());
but I need to be able to extract it for testing, which I do by running the validations imperatively:
// validation.js
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
const error = new Error();
error.message = process.env.NODE_ENV === 'development'? 'Validation Failed':'Error';
error.statusCode = !errors.isEmpty()? 422:500;
error.errors = errors.array({onlyFirstError: true});
next(error);
return error;
};
};
const validateLogin = () => {
const exampleValidationRules = [
body('email')
.isString()
.withMessage('Invalid characters, please use letters and numbers only')
];
return validation(exampleValidationRules);
}
module.exports = {
validateLogin
};
I can then call the middleware in my routes, and in my test files.
For example:
// auth.test.js
describe('Unit Tests', () => {
it('should return 422 if email validation fails', async() => {
const wrongEmailReq = { body: {email: 'nic#hotmail.com'} };
const notStringReq = { body: {email:1} };
const res = {
statusCode: 500,
status: (code) => {this.statusCode = code; return this},
};
// Function to be tested
const validationFn = validateLogin();
const wrongEmail = await validationFn(wrongEmailReq, res, ()=>{});
const notString = await validationFn(notStringReq, res, ()=>{});
expect(wrongEmail.statusCode).to.be.equal(422);
expect(wrongEmail.errors[0].param).to.be.equal('email');
expect(notString.statusCode).to.be.equal(422);
expect(notString.errors[0].param).to.be.equal('email');
return;
});
I'm just slightly confused about how I'd test the 'success' case. validationFn returns next() if successful. But that would just be undefined.
Should I just test expect(correct.statusCode).to.be.undefined;? That doesn't seem specific enough.
Also, is there actually any advantage to unit testing this function over using http-chai to run the requests? I thought perhaps it was more lightweight, but unsure.

How can I send a report via nodemailer with Cypress?

What would be the best way to use nodemailer with Cypress? I've been playing with the code bellow for the while now but with no avail. I am getting an error "cy.task('sendMail') failed with the following error:
sendAnEmail is not a function
Because this error occurred during a after all hook we are skipping all of the remaining tests."
Thanks for any tips and advices.
//Cypress config file
const { defineConfig } = require("cypress");
const sendAnEmail = require("nodemailer")
module.exports = defineConfig({
pageLoadTimeout: 180000,
e2e: {
setupNodeEvents(on, config) {
on('task', {
sendMail (message) {
return sendAnEmail(message);
}
})
},
},
});
//Nodemailer file
const sendAnEmail = (message) => {
function sendAnEmail()
const nodemailer = require('nodemailer');
const sgTransport = require('nodemailer-sendgrid-transport');
const options = {
auth: {
user: "glorioustester123#outlook.com",
pass: "********."
}
}
const client = nodemailer.createTransport(sgTransport(options));
const email = {
from: 'glorioustester123#outlook.com',
to: 'some.email#gmail.com',
subject: 'Hello',
text: message,
html: '<b>Hello world</b>'
};
client.sendMail(email, function(err, info) {
return err? err.message : 'Message sent: ' + info.response;
});
}
//The Cypress test file
/// <reference types = "cypress" />
after(() => {
cy.task('sendMail', 'This will be output to email address')
.then(result => console.log(result));
})
//zadanie A
it("navstiv stranku a vyhladaj a elementy v casti Framework Support", ()=>{
cy.visit('https://sortablejs.github.io/Sortable/#cloning')
cy.get('.col-6').find('a')
})
//zadanie B
it("navstiv stranku a vyhladaj prvy a element casti v Framework Support", ()=>{
cy.visit('https://sortablejs.github.io/Sortable/#cloning')
cy.get('[href="https://github.com/SortableJS/Vue.Draggable"]')
cy.get('.col-6').contains('a')
//contains najde prvy vyskyt, v tomto pripade to pasuje do zadania
})
//zadanie C
it("navstiv stranku vyhladaj posledny a element v casti Framework Support ", ()=>{
cy.visit('https://sortablejs.github.io/Sortable/#cloning')
cy.get('[href="https://github.com/SortableJS/ember-sortablejs"]')
})
You nodemailer file needs adjusting a bit. The is no export which is why the message sendAnEmail is not a function
const nodemailer = require('nodemailer');
const sgTransport = require('nodemailer-sendgrid-transport');
export function sendAnEmail(message)
const options = {
...
}
const client = nodemailer.createTransport(sgTransport(options));
const email = {
...
};
client.sendMail(email, function(err, info) {
return err? err.message : 'Message sent: ' + info.response;
});
}
Also, in cypress.config.js import it with a relative path
const { defineConfig } = require("cypress");
const sendAnEmail = require("./nodemailer")
and to be a clean-coder, us a different name from the npm package (something like
const sendAnEmail = require("./send-an-email")

Encountering a problem when trying to remove some code from a route to put it into a service - Node.js / Express.js

I'm having a problem right now when i want to remove some code out of my route to put it into a service. I'm just trying to follow the best practices of developing an application.
This is my route right now:
const express = require('express');
const cityRouter = express.Router();
const axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();
cityRouter.get('/:cep', async (request, response) => {
try {
const { cep } = request.params;
const value = myCache.get(cep);
if (value) {
response.status(200).send({
city: value,
message: 'Data from the cache',
});
} else {
const resp = await axios.get(`https://viacep.com.br/ws/${cep}/json/`);
myCache.set(cep, resp.data, 600);
response.status(200).send({
city: resp.data,
message: 'Data not from the cache',
});
}
} catch (error) {
return response.status(400);
}
});
module.exports = cityRouter;
I'm using axios to retrieve data from an API, where i have a variable called "cep" as a parameter and then using node-cache to cache it.
And it works with out problems:
enter image description here
But, when i try to put the same code into a service, and then call it into my route:
My service:
const axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();
function verificaCache(cep) {
return async function (request, response, next) {
const value = myCache.get(cep);
console.log(cep);
if (value) {
response.status(200).send({
city: value,
message: 'Data from the cache',
});
} else {
const resp = await axios.get(`https://viacep.com.br/ws/${cep}/json/`);
myCache.set(cep, resp.data, 600);
response.status(200).send({
city: resp.data,
message: 'Data not from the cache',
});
}
next();
};
}
module.exports = verificaCache;
My route using the service:
const express = require('express');
const cityRouter = express.Router();
const verificaCache = require('../services/VerificaCacheService');
cityRouter.get('/:cep', async (request, response) => {
const { cep } = request.params;
verificaCache(cep);
response.status(200);
});
module.exports = cityRouter;
By some reason, it doesn't work:
enter image description here
What is the problem that i can't see? I'm a beginner so i'm kinda lost right now.
You have created a high-order function by returning a function in verificaCache(), so to properly call it you need to do it like that await verificaCache(cep)(req, res), remember, the first time you call it, you have a function being returned, since you want the tasks inside of that function to be executed, you need to call it as well.
Take a reading about high-order functions here: https://blog.alexdevero.com/higher-order-functions-javascript/
My recommendation, you could just get rid of the other function you are returning to simplify your code, and let the service only handle business logic, all the http actions should be handled on the controller level:
// Service
function verificaCache(cep) {
const value = myCache.get(cep);
if (value) {
return { city: value, message: 'Data from the cache'})
}
// No need of an else statement because the
// execution will stop at the first return if the condition passes
const resp = await axios.get(`https://viacep.com.br/ws/${cep}/json/`);
myCache.set(cep, resp.data, 600);
return { city: resp.data, message: 'Data not from the cache'};
}
// Controller
cityRouter.get('/:cep', async (request, response) => {
const { cep } = request.params;
try {
const data = verificaCache(cep);
// Use json() instead of send()
response.status(200).json(data);
} catch(error) {
// Handle errors here
console.log(error);
}
});
Estamos juntos!

Code not executed after PUT route using Express

I listen to the chat event of the tmijs library, upon the !overlay chat I want to execute some code. What I want to achieve upon getting that message is:
Fetch the user
Check if the user has enough currency
Deduct currency from the user
Trigger a socket event to my react app
Everything seems to work up until the last bullet point. In my terminal it's shown that my user gets currency (called 'kluiten' in my code) deducted, but all the code that comes after it doesn't get executed.
require('dotenv').config();
const PORT = process.env.PORT || 9000;
class TwitchAPI {
constructor({io}) {
this.io = io;
this.client = new tmi.client(options);
this.client.connect();
this.handleOverlayRequest = this.handleOverlayRequest.bind(this);
this.handleChatMessage = this.handleChatMessage.bind(this);
this.client.on('chat', this.handleChatMessage);
}
handleChatMessage (channel, userstate, message) {
if(message === '!overlay') this.handleOverlayRequest(channel, userstate);
}
async handleOverlayRequest (channel, userstate) {
const requiredKluiten = 5;
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
if(foundUser.instakluiten >= requiredKluiten) {
this.client.action(channel, `${userstate[`display-name`]}, you've got enough instakluiten for this.`);
const method = `PUT`;
const payload = { 'requiredKluiten': requiredKluiten };
const body = JSON.stringify(payload);
const headers = { 'Content-Type': `application/json; charset=utf-8` };
const result = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}/decrementKluiten`, { method, body, headers });
console.log(result);
}
}
}
module.exports = TwitchAPI;
I then have an Express router:
const express = require('express');
const userController = require('../controllers/userController');
const router = express.Router();
router.route('/users/:username/decrementKluiten').put(userController.decrementKluiten);
router.route('/users/:username').get(userController.getUser);
router.route('/overview').get(userController.getOverview);
module.exports = router;
which makes sure the currency gets deducted. What I'm stuck on now is that, after all this has happened, I can't execute any code anymore after the fetch. I found though that I could execute code by resolving the promise in my route, but that feels really dirty and messes up my split up files:
router.route('/users/:username/decrementKluiten').put((req, res) => {
userController.decrementKluiten(req, res).then(x => {
console.log(x);
});
});
Is there a way to wait for my PUT to happen and still execute code after it did?
EDIT
userController.js
const {findChattersPerRole, getUserByUsername, decrementKluiten} = require('../actions');
const find = require(`lodash/find`);
const fetch = require(`isomorphic-fetch`);
const parseJSON = response => response.json();
module.exports = {
getUser: (req, res) => {
const username = req.params.username;
findChattersPerRole()
.then(chattersPerRole => {
const wantedUser = find(chattersPerRole, { username });
getUserByUsername(wantedUser.username)
.then(foundUser => {
if (foundUser) {
res.send(foundUser);
} else {
res.send(`No user has been found`);
}
});
});
},
getOverview: (req, res) => {
fetch(`https://tmi.twitch.tv/group/user/instak/chatters`)
.then(parseJSON)
.then(r => {
return res.json(r);
}).catch(err => {
console.log(err);
});
},
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten);
}
}
actions.js
(Because this contains a lot of code I try to only include the relevant parts for this post, the database calls are done using Sequelize.js)
const decrementKluiten = (username, requiredKluiten) => {
return global.db.Viewer.findOne({
where: { username }
}).then(user => {
return user.decrement({ instakluiten: requiredKluiten });
});
};
module.exports = {
decrementKluiten
};
The issue is likely that you don't respond to the HTTP request in your /users/:username/decrementKluiten route. To solve this, change the exported decrementKluiten method in userController.js-file to this:
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten)
.then(() => res.sendStatus(200))
.catch(() => res.sendStatus(500));
}
Some unrelated pointers to make your code a bit more readable, since you already use async functions in some parts of your code, but in other parts you interface directly with Promises.
The exported part of userController.js could utilize async functions:
module.exports = {
getUser: async (req, res) => {
try {
const username = req.params.username;
let chattersPerRole = await findChattersPerRole();
let wantedUser = find(chattersPerRole, { username });
let foundUser = await getUserByUsername(watnerUser.username);
if (foundUser) {
res.status(200).send(foundUser);
} else {
res.status(404).send('No user has been found');
}
} catch (e) {
res.sendStatus(500);
}
},
getOverview: async (req, res) => {
try {
let r = (await fetch('https://tmi.twitch.tv/group/user/instak/chatters')).json();
res.json(r);
} catch (e) {
res.sendStatus(500);
}
},
decrementKluiten: async (req, res) => {
try {
await decrementKluiten(req.params.username, req.body.requiredKluiten);
res.sendStatus(200);
} catch (e) {
res.sendStatus(500);
}
}
}
I've also added error handling in case something goes wrong, the server responds with a 500 Internal Server Error status code.
Judging by these lines in your TwitchAPI class:
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
I assume you've tried to do const foundUser = await fetch('...').json(). This results in an error, but you can call the retuned value's methods and properties on the same line if you wrap the await expression in parentheses, like this:
const foundUser = await (await fetch('...')).json()`
If its methods does not return a Promise (i.e being synchronous), or you want to access a property, you can do:
const something = (await doSomethingAsync()).someMethod()
const somethingElse = (await doSomethingAsync()).property
I also noticed you're using template literals (backticks, `) for most strings without doing any template interpolation, which could simply be replaced with ' (single-quotes) or " (double-quotes).

Categories