Calling the SP from MSsql issuse - javascript

I am trying to calling the SP from MSSQL and I am getting an error:
exports.ExportPropertyReport = catchAsyncErrors(async(req, res, APN, TimeTrend, MONTH_YEAR, lowrange, highrange) => {
// const APN = req.body.APN;
try {
let pool = await sql.connect(config, { requestTimeout: 30000 })
//let pool = await sql.connect(config)
let result = await pool.request()
.input('APN', sql.Float, APN)
.input('TimeTrend', sql.NVarChar, TimeTrend)
.input('MONTH_YEAER', sql.NVarChar, MONTH_YEAR)
.input('LOWRANGE', sql.VarChar, lowrange)
.input('HighRange', sql.VarChar, highrange)
.execute('dbo.Usp_ExportPropertyReport')
console.log(result.recordset);
sql.close();
res.status(200).json({
success: true,
message: `ExportPropertyReport called successfully`,
result
});
} catch (err) {
console.log(err);
res.status(500).json({
"error": true,
"message": "Error executing query"
});
}
});
error:
RequestError: Validation failed for parameter 'APN'. Invalid number.
at Request.userCallback (E:\spcall\node_modules\mssql\lib\tedious\request.js:811:19)
at Request.callback (E:\spcall\node_modules\tedious\lib\request.js:205:14)
at E:\spcall\node_modules\tedious\lib\connection.js:1982:17
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
code: 'EPARAM',
I want to get the record of this stored procedure

Related

ERR_HTTP_HEADERS_SENT error even when using return statements

I get this error when I try and send a POST request to the server:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:278:15)
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\src\api\endpoints\account\create.js:35:36
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\mongoose\lib\model.js:5214:18
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
code: 'ERR_HTTP_HEADERS_SENT'
}
The router and routes are working fine it's just the endpoint.
It will respond to the client with {code:"EMAIL_TAKEN"} which is expected but then it also tries to send the USERNAME_TAKEN thing
I have tried moving parts of the code into separate files and using different status codes.
Code:
module.exports = async (req, res) => {
const schema = require("../../models/account");
const username = req.headers.username;
const email = req.headers.email;
if(!username) {
return res.status(400).json({ "code": "NO_USERNAME" });
}
if(!email) {
return res.status(400).json({ "code": "NO_EMAIL" });
}
if(username.length > 20) {
return res.status(400).json({ "code": "USERNAME_TOO_LONG" });
}
schema.findOne({ username }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "USERNAME_TAKEN" });
}
})
schema.findOne({ email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "EMAIL_TAKEN" });
}
})
schema.findOne({ username, email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(!data) {
data = new schema({
username,
email
})
await data.save();
return res.status(200).json({ "code": "CREATED_ACCOUNT" });
}
})
}
Using return in the findOne() callbacks won't stop the other schema.findOne() calls from being made. I'd recommend awaiting on Mongoose's promise API instead to keep everything nice and flat
module.exports = async (req, res) => {
const schema = require("../../models/account");
// headers? 🤔
const username = req.headers.username;
const email = req.headers.email;
if (!username) {
return res.status(400).json({ code: "NO_USERNAME" });
}
if (!email) {
return res.status(400).json({ code: "NO_EMAIL" });
}
if (username.length > 20) {
return res.status(400).json({ code: "USERNAME_TOO_LONG" });
}
try {
if (await schema.exists({ username })) {
return res.status(409).json({ code: "USERNAME_TAKEN" });
}
if (await schema.exists({ email })) {
return res.status(409).json({ code: "EMAIL_TAKEN" });
}
const data = new schema({
username,
email,
});
await data.save();
return res.status(201).json({ code: "CREATED_ACCOUNT" });
} catch (err) {
console.error(err);
res.sendStatus(500);
}
};
On my comment about sending your username and email values via headers... typically you would send them via a POST request in the request body.
For example, client-side...
fetch("/accounts", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ username, email }),
})
and server-side...
const { username, email } = req.body;
You just need to include the appropriate request body middleware, eg
app.use(express.json());

Node / JavaScript get try variable in catch block to delete cloudinary image when there is an error

This code works fine to upload image to cloudinary.
Let's say the image is uploaded but then there is a mongodb error.
Then I want to delete the image that is already at cloudinary but how can i get the value of "cloudinary_id" in my catch block?
createGalery: async (req, res) => {
try {
const result = await cloudinary.uploader.upload(req.file.path, {
folder: 'Test'
});
let image = result.secure_url;
let cloudinary_id = result.public_id;
const newGalery = new Galery({
image,
cloudinary_id,
});
await newGalery.save();
res.json({ msg: 'successful' });
} catch (err) {
try {
console.log(cloudinary_id);
// How to delete when mongodb save fails?
//await cloudinary.uploader.destroy(result.public_id);
} catch {
return res.status(500).json({ msg: err.message });
}
return res.status(500).json({ msg: err.message });
}
}
Nest your try blocks:
createGalery: async (req, res) => {
try {
const result = await cloudinary.uploader.upload(req.file.path, {
folder: 'Test'
});
const cloudinary_id = result.public_id;
try {
const newGalery = new Galery({
image: result.secure_url,
cloudinary_id,
});
await newGalery.save();
} catch (mongoErr) {
console.log(`Removing ${cloudinary_id} due to failed save`);
await cloudinary.uploader.destroy(cloudinary_id);
throw mongoErr;
}
res.json({ msg: 'successful' });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
}
Move the declaration of your cloudinary_id variable outside of the try/catch block:
let cloudinary_id;
try {
// ...
cloudinary_id = result.public_id; // without the "let" keyword in front of it
// ...
} catch (err) {
console.log(cloudinary_id);
if (cloudinary_id) {
await deleteFromCloudinary(cloudinary_id);
// if cloudinary_id is not set, the error was thrown
// before setting it (i.e. while uploading to cloudinary)
}
}

What is the problem in the nodejs controller function?

exports.signupController = async (req, res) => {
const { phone, password } = req.body;
try {
const user = await User.findOne({ phone }).exec()
if (user) {
return res.status(400).json({
errorMessage: 'Phone Number already exists',
});
}
const newUser = new User();
newUser.phone = phone;
const salt = await bcrypt.genSalt(10);
newUser.password = await bcrypt.hash(password, salt);
await newUser.save();
return res.status(200).json({
successMessage: 'Registration success. Please login',
});
} catch (err) {
console.log('signupController error: ', err);
res.status(500).json({
errorMessage: 'Server error',
});
}};
**I upload a node application in shared hosting! **
*But an error was showing in this controller function. All the time the catch block is running on the json. The error is unhandled promise rejection. *
signup(data)
.then((response) => {
console.log('Axios signup success: ', response);
setFormData({
phone: '',
password: '',
password2: '',
loading: false,
successMsg: response.data.successMessage,
});
history.push('/signin');
})
.catch((err) => {
console.log('Axios signup error: ', err);
setFormData({
...formData,
loading: false,
errorMsg: err.response.data.errorMessage,
});
});
this is react front end event handler
import axios from 'axios';
export const signup = async (data) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const response = await axios.post('/api/auth/signup', data, config);
return response;
};
the signup api function
Mongoose queries are not promises. They have a .then() function for co and async/await as a convenience. If you need a fully-fledged promise, use the .exec() function. for example:
const query = Band.findOne({name: "Guns N' Roses"});
assert.ok(!(query instanceof Promise));
// A query is not a fully-fledged promise, but it does have a `.then()`.
query.then(function (doc) {
// use doc
});
// `.exec()` gives you a fully-fledged promise
const promise = query.exec();
assert.ok(promise instanceof Promise);
promise.then(function (doc) {
// use doc
});
If you are using exec() on your findOne query you should use:
exports.signupController = async (req, res) => {
const { phone, password } = req.body;
try {
const user = await User.findOne({ phone }).exec();
/// just a pseudo code
user.then('do your things').catch( 'log error')
const newUser = new User();
newUser.phone = phone;
const salt = await bcrypt.genSalt(10);
newUser.password = await bcrypt.hash(password, salt);
await newUser.save();
return res.status(200).json({
successMessage: 'Registration success. Please login',
});
} catch (err) {
console.log('signupController error: ', err);
res.status(500).json({
errorMessage: 'Server error',
});
}};
for more details check this out: https://mongoosejs.com/docs/promises.html#should-you-use-exec-with-await?

jest testing nodejs controller

I have the following controller
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserModel, isPasswordAllowed } from '../../models/User';
const saltRounds = 10;
function userController() {
function add(req, res) {
try {
if (req.body.administrator) {
res.status(400).json({
error: {
message: 'Bad Request',
},
});
return;
}
if (!isPasswordAllowed(req.body.password)) {
res.status(400).json({
error: {
message: 'La contraseña no cumple con los requisitos minimos',
},
});
return;
}
bcrypt.hash(req.body.password, saltRounds, async (err, hash) => {
if (err) {
res.status(500).json({ error: { code: '500', message: err.errmsg } });
return;
}
const user = new UserModel();
user.email = req.body.email.toLowerCase();
user.password = hash;
await user
.save()
.then(() => {
const token = jwt.sign(
{
username: user.email,
userId: user.id,
},
process.env.JWT_KEY,
{
expiresIn: '7d',
},
);
res.status(200).json({
message: 'Usuario Creado',
token,
email: user.email,
});
})
.catch((error) => {
if (error.code === 11000) {
res.status(400).json({
error: { code: '500', message: 'El correo ya existe' },
});
} else {
console.log(error);
res.status(500).json({ error: { code: '500', message: error.message } });
}
});
});
} catch (error) {
res.status(503).json({ error });
}
}
return {
add,
};
}
export default userController();
As you can expect this controller works great, the user is created in the database, but I have the following test:
import UserController from './UserController';
import { connect, closeDatabase, clearDatabase } from '../../__test__/db-handler';
describe('test UserController', () => {
const res = {};
beforeEach(async () => {
await connect();
res.send = jest.fn().mockReturnValue(res);
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
});
afterEach(async () => {
await clearDatabase();
});
afterAll(async () => {
await closeDatabase();
});
test('should return the expect api method', () => {
const userControllerApi = {
add: expect.any(Function),
};
expect(UserController).toMatchObject(userControllerApi);
});
test('should return 400 error bad request is body contains administrator: true', async () => {
const req = {
body: {
administrator: true,
},
};
await UserController.add(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toHaveBeenCalledWith({
error: {
message: 'Bad Request',
},
});
});
test('should return 400 error bad request is password is not allow', async () => {
const req = {
body: {
password: '123456',
},
};
await UserController.add(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toHaveBeenCalledWith({
error: {
message: 'La contraseña no cumple con los requisitos minimos',
},
});
});
// this test is not passing
test('should create an user and return a token', async () => {
const req = {
body: {
email: 'test#test.com',
password: 'Abc123456',
},
};
const expectObject = {
message: 'Usuario Creado',
email: 'test#test.com',
};
await UserController.add(req, res);
jest.useFakeTimers();
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toMatchObject(expectObject);
});
});
but the last test 'should create an user and return a token' never pass and I get the following:
● test UserController › should create an user and return a token
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: 200
Number of calls: 0
78 | jest.useFakeTimers();
79 |
> 80 | expect(res.status).toHaveBeenCalledWith(200);
| ^
81 | expect(res.json).toHaveBeenCalledTimes(1);
82 | expect(res.json).toMatchObject(expectObject);
83 | });
I also debbug this code in testing mode and as you can see in the following image, the code is enter in the res.status(200).json({ .... }), so I don't understand what it is happening here.
The problem is that you're mixing callbacks with async/await, meaning that execution of add() will be finished before the callback of bcrypt.hash has been finished. This results in res.status not to have been called yet in your test.
You can fix this by awaiting the bcrypt.hash call (it supports returning a promise by default):
// await hashing function instead of using callback
const hash = await bcrypt.hash(req.body.password, saltRounds);
const user = new UserModel();
user.email = req.body.email.toLowerCase();
user.password = hash;
// rest of the code ...

why "short_url" not showing in res.json with the unique ID?

I am doing APIs and Microservices projects on FreeCodeCamp,the "Url shortener microservice". Here is my code:
const shorturlSchema = mongoose.Schema({
originalUrl: String,
shortUrl: Number
});
const ShortUrl = mongoose.model("ShortUrl", shorturlSchema);
app.post("/api/shorturl/new", urlencodedParser, async (req, res) => {
var url = req.body.url;
console.log(url);
if (!validUrl.isWebUri(url)) {
res.json({ error: "invalid url" });
} else {
var newUrl = new ShortUrl({
originalUrl: url,
shortUrl: shortId.generator
});
try {
await newUrl.save();
} catch (err) {
console.log("error", err);
return res.json({
error: "failed to store in database"
});
}
res.json({
"origianl_url": newUrl.originalUrl,
"short_url": shortId.generate
});
}
});
app.get("/api/shorturl/:short_url?", async (req, res) => {
console.log(req.params.short_url);
const smallUrl = req.params.short_url;
if (smallUrl === undefined) res.json({ error: "undefined" });
else {
const actualUrl = await ShortUrl.findOne({ shortUrl: smallUrl });
res.redirect(actualUrl.originalUrl);
return;
}
});
It should respond with a {"original_url":"www.google.com","short_url":1}. But instead it shows {"origianl_url":"https://www.freecodecamp.org". What am I doing wrong here?
You could pass the shortUrl from the database object.
Edited code:
try {
const response = await newUrl.save();
return res.json({
"origianl_url": response.originalUrl,
"short_url": response.shortUrl
});
} catch (err) {
return res.json({
error: "failed to store in database"
});
}
Actually I made some changes in the code. In the schema:
const shorturlSchema = mongoose.Schema({
originalUrl: String,
shortUrl: {
type:String,
default:shortId.generate
}
});
Then in the app.post section
var newUrl = new ShortUrl({
originalUrl: url
});
Then finally in the res.json part of app.post
res.json({
"original_url": newUrl.originalUrl,
"short_url": newUrl.shortUrl
});
This gives me the output:
{"original_url":"https://www.freecodecamp.org","short_url":"_sAInwNXs"}
I just figured this out now. Thanks for all your help

Categories