I can't access the res.body of a POST request from the express server in a react/express stack app - express

This is the server code:
app.post("/hello", async function (req, res) {
let passedInToggleValue = false
console.log(req.body);
if (req.body === null || req.body === undefined) {
console.log("body is empty");
}
else if (!req.body.hasOwnProperty('switchBool')) {
console.log("switchBool is empty");
}
else if (req.body.switchBool) {
passedInToggleValue = true
}
const toggleValue = passedInToggleValue ? "on" : "off";
const resData = `this backend is running and the Testing On/Off Switch is ${toggleValue}`;
res.send(resData);
});
And this is the react code that calls the express API:
async function callApi() {
const url = `${process.env.REACT_APP_API_URL}/hello`;
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ switchBool: true })
};
const helloStream = await fetch(url, requestOptions);
const helloText = await helloStream.text();
console.log(helloText as string);
}
But in the terminal, console.log(req.body); shows up as undefined and if (req.body === null || req.body === undefined) gets triggered and "body is empty" appears in my terminal.

I added app.use(express.json()); to the express server code so the relevant section looks like this now:
app.use(express.json());
app.post("/hello", async function (req, res) {
let passedInToggleValue = false
console.log(req.body);
if (req.body === null || req.body === undefined) {
console.log("body is empty");
}
else if (!req.body.hasOwnProperty('switchBool')) {
console.log("switchBool is empty");
}
else if (req.body.switchBool) {
passedInToggleValue = true
}
const toggleValue = passedInToggleValue ? "on" : "off";
const resData = `this backend is running and the Testing On/Off Switch is ${toggleValue}`;
res.send(resData);
});

Related

Jest/ExpressJS - TypeError: Cannot read properties of undefined - Inner https.request call set to const var - var.on after https call can't be read

Can't get the last four lines in the helpers.js below to run for the test. The webpage works great, but I can't get the tests to pass/the mocks correct. This is my first time using Jest and unit testing in general, so there may be a fundamental understanding issue as well.
Function in helpers.js
exports.get_logout = (req, resp) => {
// sent to backend
const options = {
hostname: backend_hostname,
port: backend_port,
method: 'POST',
path: '/api/logout',
ca: ca,
headers: {
'Content-Type': 'application/json',
}
};
// set cookie
options.headers.Cookie = `ws_sid=${req.cookies.ws_sid}`
// send to backend to end session
const https_req = https.request(options, (response) => {
const response_status = response.statusCode;
const response_headers = response.headers;
response.on('data', (d) => {
process.stdout.write(d);
});
response.on('end', () =>{
switch (response_status) {
case 200:
console.log('Successfully logged out')
resp.clearCookie('ws_sid', {path: '/'})
resp.redirect('/')
break;
case 400:
console.log('Error on logout!')
resp.redirect('/');
break;
case 403:
console.log('User not logged in!')
resp.redirect('/');
break;
}
})
});
https_req.on('error', (e) => {
console.error(e);
});
https_req.write(JSON.stringify({}))
https_req.end();
}
the issue in website.test.js, is that the https_req.on is giving a TypeError:Cannot read properties of undefined (reading 'on')
Test here:
const helpers = require('./helpers');
const https = require('node:https');
// Mock for express request parameter
let mockRequest = (sessionData, method, body) => {
return {
session: { data: sessionData, cookie: {_expires: 'test_expire'}},
method: method,
body: {username:"", password:""},
cookies: {ws_sid:""},
};
};
// Mock for express response parameter
let mockResponse = () => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
res.send = jest.fn().mockReturnValue(res);
res.sendStatus = jest.fn().mockReturnValue(res)
res.render = jest.fn().mockReturnValue(res)
res.redirect = jest.fn().mockReturnValue(res)
return res;
};
jest.mock('https');
https.request = jest.fn();
https.on = jest.fn();
https.end = jest.fn();
test('should redirect to / if user successfully logged out', async () => {
const req = mockRequest({ 'username': 'all' });
const resp = mockResponse();
helpers.get_logout(req, resp);
expect(resp.redirect).toHaveBeenCalledWith('/');
});
Any help greatly appreciated.
I have tried mocking the https_req object - but kept getting errors with the implementation (not a function, can't access before initialization). I tried adding the https_req object to the mockRequest with (on, end) but got the same TypeError

react native fetch hook and refresh jwt token

i have made a custom hook to fetch data from api, but the token only valid for 5 second.
so i made this hook
the problem is when i call the hooks from my page it called many time and the refresh token already expired
when i access the api i will check the response first if the token invalid i tried to refresh my token using handleRefreshToken
nb : im using useContext for my state management
import React, {useEffect, useState, useContext} from 'react';
import {View, StyleSheet} from 'react-native';
import {AuthContext} from '../Auth/Context';
import AsyncStorage from '#react-native-community/async-storage';
import {urlLogin, URLREFRESHTOKEN} from '../Configs/GlobaUrl';
const FetchData = () => {
const {loginState, authContext} = useContext(AuthContext);
const [data, setData] = useState([]);
const [message, setMessage] = useState('');
const [loading, setIsLoading] = useState(false);
const {dispatchRefreshToken} = authContext;
const handleRefreshToken = async (callbackUrl, callbackBody) => {
const refBody = {
client_id: loginState.ipAddress,
ipAddress: loginState.ipAddress,
employee_id: loginState.userData.Pegawai_Id,
jwttoken: loginState.userToken,
refresh_tokenn: loginState.refreshToken,
};
console.log('======refreshtokencalled==========');
console.log(refBody.refresh_tokenn, '<=refresh token');
console.log(refBody.jwttoken, '<=jwt token');
let response = await fetch(URLREFRESHTOKEN, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(refBody),
redirect: 'follow',
});
let result = await response.json();
console.log(result, ' ini result');
if (
result.item3 !== 'refresh token gagal' &&
result.item3 !== 'refresh token sudah tidak berlaku'
) {
let refresh = result.item2;
let token = result.item1;
// the backend doesnt send any succes / error code only item1 for token, //item2 refresh token and item3 for error
dispatchRefreshToken(token, refresh);
await AsyncStorage.setItem('refreshToken', refresh);
await AsyncStorage.setItem('token', token);
return getData(callbackUrl, callbackBody);
} else {
return null;
}
};
const getData = async (url, body) => {
setIsLoading(true);
let result;
try {
let response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${loginState.userToken}`,
},
body: JSON.stringify(body),
redirect: 'follow',
});
if (response.status == '401') {
let refreshResult = await handleRefreshToken(url, body);
console.log(refreshResult);
} else {
result = await response.json();
console.log(result);
console.log(loginState.refreshToken);
if (result.code == '1') {
setData(result.data);
setIsLoading(false);
} else {
throw result;
}
}
} catch (err) {
setData([]);
console.log(err, 'masuk error usefetchbybutton');
console.log(err.message, err.code);
setIsLoading(false);
setMessage(err);
}
};
return {
data: data,
message: message,
loading: loading,
getData: getData,
};
};
export default FetchData;
this is my dispatch refresh token
const authContext = useMemo(
() => ({
logIn: async (token, userData, refreshToken) => {
console.log(token, '<>', refreshToken, 'ini memoisa');
dispatch({
type: 'LOGIN',
token: token,
userData: userData,
refreshToken: refreshToken,
});
},
logOut: () => {
AsyncStorage.clear((error) => {
console.log(error);
});
dispatch({type: 'LOGOUT'});
},
dispatchRefreshToken: (userToken, refreshToken) => {
console.log(refreshToken, '=refresh dispatch=');
console.log(userToken, '=userToken dispatch=');
dispatch({
type: 'REFRESHTOKEN',
userToken: userToken,
refreshToken: refreshToken,
});
},
}),
[],
);
my reducer function
const loginReducer = (prevState, action) => {
switch (action.type) {
some case ...
case 'REFRESHTOKEN':
return {
...prevState,
userToken: action.userToken,
refreshToken: action.refreshToken,
};
}
};
Use recursion. The pseudo code is as follows
const getData = async (args, times) => {
// try to fetch data
const data = await Api.fetch(args);
// if token need to be refreshed.
if (check401(data)) {
// Use variable times to prevent stack overflow.
if (times > 0) {
// refresh the token
await refreshToken()
// try again
return getData(args, times - 1);
} else {
throw new Error("The appropriate error message")
}
}
return dealWith(data)
}
The logical above can be encapsulated to all your api. Like this
const wrapApi = (api) => {
const wrappedApi = async (args, times) => {
const data = await api(args);
// if token need to be refreshed.
if (check401(data)) {
// Use variable times to prevent stack overflow.
if (times > 0) {
// refresh the token
await refreshToken()
// try again
return wrappedApi(args, times - 1);
} else {
throw new Error("The appropriate error message")
}
}
return dealWith(data)
}
return wrappedApi;
}

timeout async callback testing with sinon jest supertest to simulate error 500 on express api

I am testing an api with all http 500 errors.
Here I try to use sinon.stub to test on a failing server and get a 500 error, but I get a timeOut async callback, or if I use my app a successfull 200 response statusCode as if sinon.stub has no effect. I must miss something and I am stucked...
would you see a horrifying error below ?
thanks for your precious help
process.env.NODE_ENV = "test";
const app = require("../../app");
const request = require("supertest");
const sinon = require("sinon");
// /************************** */
const usersRoute = require("../../routes/Users");
const express = require("express");
const initUsers = () => {
const app = express();
app.use(usersRoute);
return app;
};
describe("all 5xx errors tested with stub", function () {
it("should return a 500 when an error is encountered", async (done) => {
let secondApp;
sinon.stub(usersRoute, "post").throws(
new Error({
response: { status: 500, data: { message: "failed" } },
})
);
secondApp = initUsers(); //==========> Timeout Async Callback
//secondApp = require("../../app"); //==============> gives a 200 instead of 500
const fiveHundredError = await request(secondApp)
.post("/users/oauth?grant_type=client_credentials")
.send({
username: "digitalAccount",
password: "clientSecret",
});
expect(fiveHundredError.statusCode).toBe(500);
//sinon.restore();
done();
});
});
app is using express.Router to get users route :
const express = require("express");
const router = express.Router();
const axios = require("axios");
router.post("/users/oauth", async (req, res) => {
//if (all missing parts)
//else {
try {
if (req.fields) {
const response = await axios.post(
`${base_url}oauth/token?grant_type=${req.query.grant_type}`,
{},
{
auth: {
username: req.fields.username,
password: req.fields.password,
},
}
);
res.json(response.data);
}
} catch (error) {
return res.status(error.response.status).json(error.response.data);
}
}
});
module.exports = router;
See server.js :
const app = require("./app");
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`server starting on port ${port}!`));
and app.js :
// private environment:
require("dotenv").config();
const express = require("express");
const formidableMiddleware = require("express-formidable");
const cors = require("cors");
const app = express();
app.use(formidableMiddleware());
app.use(cors());
const usersRoute = require("./routes/Users");
app.use(usersRoute);
app.get("/", (req, res) => {
res.status(200).send("Welcome to Spark Admin back end!");
});
app.all("*", (req, res) => {
return res.status(404).json({ error: "Web url not found" });
});
module.exports = app;
I finally opted for 'nock' and deleted 'sinon'
const nock = require("nock");
const axios = require("axios");
describe("POST login: all 5xx errors tested with nock", function () {
it("should return a 500 when an error is encountered", async (done) => {
const scope = nock("http://localhost:5000")
.post(
"/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
)
.reply(500, {
response: {
statusCode: 500,
body: { error: "AN ERROR OCCURED" },
},
});
try {
await axios.post(
"http://localhost:5000/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
);
} catch (e) {
expect(e.response.status).toBe(500);
}
done();
});
});

Jest testing of async middleware for authentication

I'm using a static array to scaffold a user table, prior to refactoring with actual postgres db and some fetch()-ing code. At present, the tests work, but obviously they are working synchronously. Here's the placeholder API code:
// UserAPI.js
let findUserById = (credentials = {}) => {
const { userId } = credentials
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
And an example test as follows:
// excerpt from TokenAuth.test.js
const UserAPI = require('../lib/UserAPI')
describe('With TokenAuth middleware', () => {
beforeEach(() => {
setStatus(0)
})
it('should add user to req on authorised requests', () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})
})
This runs fine, and along with other tests gives me the coverage I want. However, I now want to check that the tests will deal with the async/await nature of the fetch() code I'm going to use for the proper UserAPI.js file. So I re-write the placeholder code as:
// UserAPI.js with added async/await pauses ;-)
let findUserById = async (credentials = {}) => {
const { userId } = credentials
// simulate url resolution
await new Promise(resolve => setTimeout(() => resolve(), 100)) // avoid jest open handle error
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
... at which point I start getting some lovely failures, due I think it's returning unresolved promises.
My problem is two-fold:
How should I alter the UserAPI.test.js tests to deal with the new async nature of findUserByCredentials() ?
Am I ok in my assumption that ExpressJS is happy with async functions as request handlers? Specifically, due to the async nature ofUserAPI.findUserByCredentials is this ok?
Main App.js uses curried UserAPI.byId() for the findUserById.
// App.js (massively simplified)
const express = require('express')
const TokenAuth = require('./middleware/TokenAuth')
const RequireAuth = require('./middleware/RequireAuth')
const UserAPI = require('./lib/UserAPI')
let router = express.Router()
const app = express()
app.use(TokenAuth(UserAPI.byId))
app.use(RequireAuth)
app.use('/users', UserRouter)
module.exports = app
My TokenAuth middleware would now run along these lines:
// TokenAuth.js (simplified)
const jwt = require('jsonwebtoken')
require('dotenv').config()
const signature = process.env.SIGNATURE
let TokenAuth = findUserById => async (req, res, next) => {
let header = req.headers.authorization || ''
let [type, token] = header.split(' ')
if (type === 'Bearer') {
let payload
try {
payload = jwt.verify(token, signature)
} catch (err) {
res.sendStatus(401)
return
}
let user = await findUserById(payload)
if (user) {
req.user = user
} else {
res.sendStatus(401)
return
}
}
next()
}
module.exports = TokenAuth
A partial answer us simply to add an async/await on the middleware call:
it('should add user to req on authorised requests', async () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
await myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})

Axios response in route before each

I'm new to Vue so don't know how I achieve the following:
I want an Axios response so I can match the response token with a locally stored token because I found that if I modify the token value in local store, the user can still navigate from any secure route.
Is there any other method to secure this?
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)){
const authUser = localStorage.getItem('authUser')
console.log(authUser);
var session='here i want axios response value'
if (!authUser===session){
console.log('here log');
next({ path: '/', })
} else {
console.log('else redirect');
next()
}
} else {
console.log('bhar else redirect');
next()
}
});
Here is my login.vue success loginpost method:
loginPost(){
var _this = this
var vm = this.hasErrors
var _vm = this.errorMessage
var log = {
companyname: this.loginData.companyname,
username: this.loginData.both,
password: this.loginData.password,
}
this.$http.post('http://localhost:3000/api/users/login', log)
.then(function (response) {
if(response.status=== 200){
this.$store.commit('authUser', response.data.id)
localStorage.setItem('authUser', response.data.id)
_this.$router.push('/authuser');
}else{
console.log('unknow');
}
})
.catch(function (error) {
var errors = error.response;
if(errors.statusText === 'Unprocessable Entity'){
if(errors.data){
if(error.response.data.error.details.messages.username){
vm.both = true
_vm.both = error.response.data.error.details.messages.username[0]
}
}
}
});