How to do e2e test in nestjs using mock data - javascript

I have an api in controller of nestjs.
First it checks wheter there is a center info or no.
After that, it makes a client.
#Post('/client')
public async createClient(
#Body() createClientDto: CreateClientDto,
#Req() req,
#Res() res,
) {
const { centerId } = createClientDto;
let center = null;
try {
center = await this.centersReadService.findCenter(centerId);
} catch (err) {
throw new HttpException(
{
errorLog: err.message,
},
500,
);
}
if (!center) {
throw new HttpException(
{
popup: 'There is no center"
},
500,
);
}
await this.clientCreateService.createClient(createClientDto)
return res.json({})
}
I made e2e test about this api.
It runs on 'docker-compose', so this app-test is running on localhost:20000.
I hope that it dosen't run new app with new port.
In this test, I want to get 'center=null' by findCenter method regardless of 'centerId' of 'createClientDto'.
Is is possible that some methods return fixed value using e2e test?
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import { t_CreateClientDto } from './mock-data';
describe('Masters Clients Post (e2e)', () => {
let app: INestApplication;
const testUrl = 'http://localhost:20000';
it('can't create a client since there is no center.', () => {
return request(testUrl)
.post('/users/v1/masters/client')
.send(t_CreateClientDto)
.expect('Content-Type', /json/)
.expect((res) => {
expect(res.body.popup).toBe('There is no center');
})
.expect(500);
});
});

Related

Generic implementation of backend crud actions - react typescript

I have a MERN project, using MongoDB, and I wish to turn it into a generic form, so for example it could also retrieve data from Monday's API for example.
I am a bit confused as for how would it be the correct, efficient way of doing so.
Right now, my backend consists mainly of the following folders and files:
server.ts: responsible for connecting my mongoose to MongoDB:
import express, { Express, Request, Response } from "express";
import dotenv from "dotenv";
import mongoose from "mongoose";
import cors from "cors";
const issueRoutes = require("./routes/issueRoutes");
const usersRoutes = require("./routes/usersRoutes");
dotenv.config();
mongoose.set("strictQuery", false);
const app: Express = express();
const port = process.env.PORT;
const uri: string = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PW}#cluster0.n2k7xvu.mongodb.net/${process.env.MONGO_DB}`;
app.use(cors());
app.use(express.json());
app.use(issueRoutes);
app.use(usersRoutes);
mongoose.connect(uri).then(() =>
app.listen(process.env.PORT, () => {
console.log(
`[server]: Server is running at https://localhost:${process.env.PORT}`
);
})
);
Three folders: Models/Routes/Controllers, which are implemented in a standard way, intended to use MongoDB
Example of my issueController, responsible of handling in the backend the api requests:
//#ts-nocheck
import { Response, Request } from "express";
import IssueInterface from "../types/IssueInterface";
import Issue from "../models/issueModel";
import { ref, uploadBytes, listAll } from "firebase/storage";
var ObjectID = require("mongodb").ObjectID;
export const getIssueById = async (
req: Request,
res: Response
) => {
try {
console.log("i'm in issue by id backend");
const { id } = req.params;
const body = req.body;
const issue: IssueInterface | null = await Issue.findOne({ _id: id });
res.status(200).json(issue);
} catch (err) {
res.status(404).json({ message: "error getIssueById" });
}
};
export const getAllIssues = async (
req: Request,
res: Response
) => {
try {
const issues: IssueInterface[] = await Issue.find({});
res.status(200).json({ issues, msg: "getAllIssues" });
} catch (err) {
res.status(500).json({ msg: "failed getting issues' images", error: err });
}
};
//insert issue. retrieve the object id returned by mongodb. add image to firebase storage with id of the issue.
export const createIssue = async (
req: Request,
res: Response
) => {
try {
const answerDb = await Issue.collection.insertOne(req.body);
res.json(answerDb.insertedId);
} catch (err) {
res.status(500).json({ message: "error createIssue" });
}
};
export const updateIssueById = async (
req: Request,
res: Response
) => {
try {
const { id } = req.params;
const body = req.body;
console.log("in update issue by id: ", id);
const updateIssue: IssueInterface | null = await Issue.findByIdAndUpdate(
{ _id: id },
body
);
const allIssue: IssueInterface[] = await Issue.find();
res.status(200).json({
message: "User updated!",
issue: updateIssue,
issues: allIssue,
});
} catch (err) {
res.status(500).json({ message: "error updateIssueById" });
}
};
// getAllIssuesSortedByCompanyName
// getAllIssuesSortedByIssueDate
// getAllIssuesSortedBySeverity
export const getAllIssuesSortedBySeverity = async (
req: Request,
res: Response
) => {
try {
const allIssue: IssueInterface[] = await Issue.find().sort("severity");
res.status(200).json({
message: "Issues retrieved sorted by severity!",
issues: allIssue,
});
} catch (err) {
res.status(500).json({ message: "error getAllIssuesSortedBySeverity" });
}
};
export const getAllIssuesSortedByCompanyName = async (
req: Request,
res: Response
) => {
try {
const allIssue: IssueInterface[] = await Issue.find().sort("company_name");
res.status(200).json({
message: "Issues retrieved sorted by company_name!",
issues: allIssue,
});
} catch (err) {
res.status(500).json({ message: "error getAllIssuesSortedByCompanyName" });
}
};
export const getAllIssuesSortedByIssueDate = async (
req: Request,
res: Response
) => {
try {
const allIssue: IssueInterface[] = await Issue.find().sort(
"start_issue_date"
);
res.status(200).json({
message: "Issues retrieved sorted by start_issue_date!",
issues: allIssue,
});
} catch (err) {
res.status(500).json({ message: "error getAllIssuesSortedByIssueDate" });
}
};
export const addNewIssueMessage = async (
req: Request,
res: Response
) => {
try {
const issue = await Issue.findByIdAndUpdate(
req.body._id,
{
issue_messages: req.body.issue_messages,
},
{ $currentDate: { lastModified: true } }
);
res
.status(200)
.json({ status: 200, data: issue, timeStamp: ObjectID.timeStamp });
} catch (err) {
console.log("error creating new issue: ", err);
}
};
Considering the fact that Controllers is made out of 'many' small functions, how would you make it generic?
I'm missing a piece in the puzzle.
Regards :_)

Cannot set headers after they are sent to the client - Express JS

I am pretty much new to node / express and following a youtube tutorial to build a MERN Stack app. But my node server is giving this error
I tried restarting server many times it happening again and again. I got the idea it happens when we send two responses for one request but I don't think its the case here.
Btw here is the route it is pointing to in the error (in the try catch error response line)
// GET RANDOM
router.get("/random", verify, async (req, res) => {
const type = req.query.type;
let movie;
try {
if (type === "series") {
movie = await Movie.aggregate([
{ $match: { isSeries: true } },
{ $sample: { size: 1 } },
]);
} else {
movie = await Movie.aggregate([
{ $match: { isSeries: false } },
{ $sample: { size: 1 } },
]);
}
res.status(200).json(movie); //checked here by console logging it comes here only once
} catch (err) {
res.status(500).json(err); //the error pointing to this line
}
});
Just in case, here is the verify function code:
function verify(req,res,next) {
const authHeader = req.headers.token;
if(authHeader){
const token = authHeader.split(' ')[1];
jwt.verify(token,process.env.SECRET_KEY,(err,user) => {
if(err) res.status(403).json("Token is not valid");
req.user = user;
next();
})
} else{
return res.status(401).json("Unauthorized");
}
}

How to prevent re-render when using sockets on React?

I have this problem with my code, it likes to re-render and reconnect to the server multiple times, and I'd like to have it so it'll connect when they log in, meaning that it would have to use my "useAuth()" function which uses a different context.
Is there any way to prevent the re-render so it doesn't reconnect multiple times and only connects once?
This is my code:
import React, { useEffect } from "react";
import { useAuth } from "./AuthContext";
import { io } from "socket.io-client";
const SocketContext = React.createContext({});
export const SocketProvider = ({ children }) => {
const { isAuthenticated, user } = useAuth();
useEffect(() => {
if (isAuthenticated) {
const socket = io("ws://localhost:3002", {
reconnectionDelayMax: 10000,
auth: {
token: user.socketAuth,
},
});
socket.on("connect_error", (err) => {
if (err instanceof Error) {
console.log(err.message);
console.log(err.data);
}
});
}
}, []);
return <SocketContext.Provider value="">{children}</SocketContext.Provider>;
};
Never mind, I fixed the issue, it was working how it was supposed to be. It was the server that wasn't accepting the connection and it just kept logging the ID of the socket and not actually letting it connect.
io.use(async (socket, next) => {
const user = await User.findOne({ socketAuth: socket.handshake.auth.token });
if (!user) {
const err = new Error("Unauthorized");
err.data = { message: "Unauthorized, please try again later." };
next(err);
}
next(); // New line added to allow connection
});

Async Function working in Express but not NestJs

I initially created a little express server to run a report and file write function.
var ssrs = require('mssql-ssrs');
var fs = require('fs');
const express = require('express')
const app = express()
const port = 3001
app.get('/', (req, res) => {
reportCreation();
res.send('File Created');
})
app.get('/api', (req, res) => {
reportCreation();
res.json({'File Created': true});
})
app.listen(port, () => {
console.log(`Report Api listening at http://localhost:${port}`)
})
The function reportCreation() is an async function which gets a report from a SSRS. This works fine
async function reportCreation() {
var serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
var reportPath = '/ApplicationPortalReports/TestReportNew';
var fileType = 'word';
var parameters = { ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 }
var auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) console.log('Data written');
});
} catch (error) {
console.log(error);
}
I have been working a lot with NestJs recently and wanted to use the same function but within a NestJs service.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
// excel = xlsx
// word = doc
// pdf = pdf
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) { console.log('Data written');
return 'File Written Succesfully'}
});
} catch (error) {
console.log(error);
return 'File Write Error'
}
}
}
As you can see the files are almost identical, but when I run it through NestJs I get an error which looks like a problem with the line
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
not awaiting. Why does this work with Express and not NestJS? Below is the error from NestJs
buffer.js:219
throw new ERR_INVALID_ARG_TYPE(
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer,
Array, or Array-like Object. Received type undefined
at Function.from (buffer.js:219:9)
at new Buffer (buffer.js:179:17)
at Object.createType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\ntlm.js:172:19)
at sendType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:77:23)
at Immediate._onImmediate (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:101:4)
within the mssql-ssrs node package the getReportByURL looks like this
async function getReportByUrl(reportPath, fileType, params, auth) {
try {
var config = {
binary: true, // very important
username: auth.userName,
password: auth.password,
workstation: auth.workstation,
domain: auth.domain,
url: soap.getServerUrl()
+ "?" + (testReportPath(reportPath).replace(/\s/g, '+'))
+ "&rs:Command=Render&rs:Format=" + reportFormat(fileType)
+ formatParamsToUrl(params)
};
} catch (err) { report.errorHandler(err) }
return new Promise((resolve, reject) => {
config.url = encodeURI(config.url);
httpntlm.post(config, function (err, res) {
if (res.statusCode === 500) { reject(res) }
if (err || res.statusCode !== 200) { reject(err) }
else { resolve(res.body) }
})
})
}
Here is the app.controller.ts
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): Promise<string> {
return this.appService.getReport();
}
}
This is not an answer for the question. But after I see your code, I can see an error you will face in future if await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth) failed. Actually you see above error because of this.
The way you used the try catch is really bad.
Here's the way I code it.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
const report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
return new Promise(function(resolve, reject) {
fs.writeFile('ReportApiTest.doc', report, , function(err) {
if (err) reject(err);
resolve("File Created");
});
});
}
And in my controller
#POST
async writeFile() {
try {
const res = await this.appService.getReport();
return res;
} catch(err) {
// handle your error
}
}
I had fudged the code in the node_module changing the userName variable to username and had not done the same in the NestJS version. I forgot I had done that so now it is working.

Using Node.js to connect to a REST API

Is it sensible to use Node.js to write a stand alone app that will connect two REST API's?
One end will be a POS - Point of sale - system
The other will be a hosted eCommerce platform
There will be a minimal interface for configuration of the service. nothing more.
Yes, Node.js is perfectly suited to making calls to external APIs. Just like everything in Node, however, the functions for making these calls are based around events, which means doing things like buffering response data as opposed to receiving a single completed response.
For example:
// get walking directions from central park to the empire state building
var http = require("http");
url = "http://maps.googleapis.com/maps/api/directions/json?origin=Central Park&destination=Empire State Building&sensor=false&mode=walking";
// get is a simple wrapper for request()
// which sets the http method to GET
var request = http.get(url, function (response) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
data,
route;
response.on("data", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
// finished transferring data
// dump the raw data
console.log(buffer);
console.log("\n");
data = JSON.parse(buffer);
route = data.routes[0];
// extract the distance and time
console.log("Walking Distance: " + route.legs[0].distance.text);
console.log("Time: " + route.legs[0].duration.text);
});
});
It may make sense to find a simple wrapper library (or write your own) if you are going to be making a lot of these calls.
Sure. The node.js API contains methods to make HTTP requests:
http.request
http.get
I assume the app you're writing is a web app. You might want to use a framework like Express to remove some of the grunt work (see also this question on node.js web frameworks).
/*Below logics covered in below sample GET API
-DB connection created in class
-common function to execute the query
-logging through bunyan library*/
const { APIResponse} = require('./../commonFun/utils');
const createlog = require('./../lib/createlog');
var obj = new DB();
//Test API
routes.get('/testapi', (req, res) => {
res.status(201).json({ message: 'API microservices test' });
});
dbObj = new DB();
routes.get('/getStore', (req, res) => {
try {
//create DB instance
const store_id = req.body.storeID;
const promiseReturnwithResult = selectQueryData('tablename', whereField, dbObj.conn);
(promiseReturnwithResult).then((result) => {
APIResponse(200, 'Data fetched successfully', result).then((result) => {
res.send(result);
});
}).catch((err) => { console.log(err); throw err; })
} catch (err) {
console.log('Exception caught in getuser API', err);
const e = new Error();
if (err.errors && err.errors.length > 0) {
e.Error = 'Exception caught in getuser API';
e.message = err.errors[0].message;
e.code = 500;
res.status(404).send(APIResponse(e.code, e.message, e.Error));
createlog.writeErrorInLog(err);
}
}
});
//create connection
"use strict"
const mysql = require("mysql");
class DB {
constructor() {
this.conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'pass',
database: 'db_name'
});
}
connect() {
this.conn.connect(function (err) {
if (err) {
console.error("error connecting: " + err.stack);
return;
}
console.log("connected to DBB");
});
}
//End class
}
module.exports = DB
//queryTransaction.js File
selectQueryData= (table,where,db_conn)=>{
return new Promise(function(resolve,reject){
try{
db_conn.query(`SELECT * FROM ${table} WHERE id = ${where}`,function(err,result){
if(err){
reject(err);
}else{
resolve(result);
}
});
}catch(err){
console.log(err);
}
});
}
module.exports= {selectQueryData};
//utils.js file
APIResponse = async (status, msg, data = '',error=null) => {
try {
if (status) {
return { statusCode: status, message: msg, PayLoad: data,error:error }
}
} catch (err) {
console.log('Exception caught in getuser API', err);
}
}
module.exports={
logsSetting: {
name: "USER-API",
streams: [
{
level: 'error',
path: '' // log ERROR and above to a file
}
],
},APIResponse
}
//createlogs.js File
var bunyan = require('bunyan');
const dateFormat = require('dateformat');
const {logsSetting} = require('./../commonFun/utils');
module.exports.writeErrorInLog = (customError) => {
let logConfig = {...logsSetting};
console.log('reached in writeErrorInLog',customError)
const currentDate = dateFormat(new Date(), 'yyyy-mm-dd');
const path = logConfig.streams[0].path = `${__dirname}/../log/${currentDate}error.log`;
const log = bunyan.createLogger(logConfig);
log.error(customError);
}
A more easy and useful tool is just using an API like Unirest; URest is a package in NPM that is just too easy to use jus like
app.get('/any-route', function(req, res){
unirest.get("https://rest.url.to.consume/param1/paramN")
.header("Any-Key", "XXXXXXXXXXXXXXXXXX")
.header("Accept", "text/plain")
.end(function (result) {
res.render('name-of-the-page-according-to-your-engine', {
layout: 'some-layout-if-you-want',
markup: result.body.any-property,
});
});

Categories

Resources