Optimization requests/responses in react/nodejs - node.js

so I have problem with optimizing code, accordingly to DRY rule.
I have 2 requests, 2 responses, 2 GETS, 2 POSTS,
I think I should use loop, which changing the number of iterator, but I have problem with syntax.
Here is my fetch from axios:
componentDidMount() {
fetch('http://localhost:3001/getcounterEbook1')
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({counterebook1: count.count});
}).catch(err => {
});
fetch('http://localhost:3001/getcounterEbook2')
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({counterebook2: count.count});
}).catch(err => {
});
}
Here is handleClick1,2 function:
handleClick1(e) {
console.log("Sending ...")
let data = {
clicked: true,
}
axios.post('http://localhost:3001/counterEbook1', data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
handleClick2(e) {
console.log("Sending ...")
let data = {
clicked: true,
}
axios.post('http://localhost:3001/counterEbook2', data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
How can I transform it into loop?

One possibility is to use an Object, call it myObject, and use map like so:
const myObject = {
'counterebook1' : 'http://localhost:3001/getcounterEbook1',
'counterebook2' : 'http://localhost:3001/getcounterEbook2',
};
class Foo extends Component {
....
for (let [key, url] of Object.entries(myObject)) {
fetch(url)
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({key: count.count});
}).catch(err => {
});
}
...
You can consolidate handleClicks by first retrieving the source that triggered the event, and then using that same object
handleClick = (e) => {
console.log("Sending ...")
const url = myObject[e.target.id];
let data = {
clicked: true,
}
axios.post(url, data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
Note that we're pulling the id prop frome.targetand referencing the key/value pair inmyObject. This means that the element rendered must have theid` prop set to some key in myObject:
For example:
<button onClick={this.handleClick} id={key}>
`Button for ${key}`
</button>
Hope this help!

Related

How do I handle errors properly with fast CSV?

In the code below, I want to check if the headers are valid and then stop reading the stream if they are and send an error back to the client (via my ErrorHandler function). I see the following in my console:
UnhandledPromiseRejectionWarning: Error: Please check your column headers.
Why is that?
uploadExercisesViaFile = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
let checker = (arr, target) => target.every((v) => arr.includes(v));
const columns = ["name", "link", "description"];
const results = [];
let errors: string[] = [];
const parseCSV = parseString(req.file.buffer.toString(), {
headers: true,
maxRows: 5,
})
.on("headers", (header) => {
if (!checker(header, columns)) {
errors.push("Please check your column headers.");
parseCSV.end();
}
})
.on("data", async (row) => {
console.log(row);
results.push(row);
})
.on("error", (error) => {
errors.push(error.message);
})
.on("end", async (rowCount) => {
// Check if errors
if (errors.length > 0) {
console.log(errors);
throw new ErrorHandler(400, errors[0]);
}
// Upload
const uploadedExercises = await Exercises.batchUploadExercise(
results.map((r) => {
return { ...r, access: req.body.access, coach: req.user.id };
})
);
res.status(200).json({ rowCount: rowCount });
return;
});
} catch (e) {
next(e);
}
};

Send KnexJS error message to the frontend

I am having trouble sending an error to the front end when a csv file is uploaded and the numbers already exist in the database. The backend is logging an error that the primary key value already exist, but the code I have written tells the front end that the file uploaded just fine.
Code snippet:
router.post('/:program/upload', upload.single('testUpload'), (req, res, next) => {
try {
CSVtoPSQL(req.params.program, req.file.filename)
return res.status(201).json({
message: 'File Uploaded Just fine :)'
});
} catch (error) {
return res.status(500).json({
message: error
})
}
});
const CSVtoPSQL = (program, filePath) => {
let stream = fs.createReadStream(path.resolve(__dirname, '../files', filePath));
let csvData = [];
let csvStream = csv
.parse({ headers: false })
.on('error', error => console.error(error))
.on('data', (data) => {
csvData.push(data.toString());
})
.on('end', () => {
csvData.forEach(item => {
queries.upload(program, item)
.then(() => {
console.log('QR Code Added: ' + item);
}).catch((err) => {
console.log(`oopsie: ${err}`);
});
})
});
stream.pipe(csvStream);
}
Pretty confident the issue is with my poor understanding of promises.
As expected, I wasn't handling my promises correctly. I've updated the code a bit and now it responds with 2 arrays of successful uploads and errored uploads.
router.post('/:program/upload', upload.single('testUpload'), async (req, res, next) => {
try {
const result = await CSVtoPSQL(req.params.program, req.file.filename)
return res.status(201).json(result);
}
catch (error) {
return res.status(500).json({
message: error,
})
}
});
const CSVtoPSQL = (program, filePath) => {
let stream = fs.createReadStream(path.resolve(__dirname, '../files', filePath));
let csvData = [];
return new Promise((resolve) => {
const results = {
seccess: [],
error: [],
}
let csvStream = csv
.parse({ headers: false })
.on('error', error => console.error(error))
.on('data', (data) => {
csvData.push(data.toString());
})
.on('end', async () => {
await Promise.all(
csvData.map(async (item) => {
try{
await queries.upload(program, item);
results.success.push(item);
console.log('QR Code Added: ' + item);
}
catch (error) {
console.log(`oopsie: ${error}`)
results.error.push(item);
}
})
)
resolve(results);
});
stream.pipe(csvStream);
})
}

How to stub the same method in two test-cases using Sinon?

I am using Node, Mongoose, Sinon, Mocha.
In the DAO layer, I have methods named methodA, methodB. In the service layer, I have servMethodA (calls methodA), servMethodB (calls methodB), servMethodC. Now, servMethodC calls the methodA from DAO and then I have a call to methodB nested in it.
In the test cases for the service layer, I have already stubbed methodA and methodB. How do I stub them again for the test-case for servMethodC?
These are my service methods.
function findLikeByPostIdAndUserId(postId, userId) {
return new Promises((resolve, reject) => {
likeDislikeDao.findLikeByPostIdAndUserId(postId, userId).
then((data) => {
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
function findDislikeByPostIdAndUserId(postId, userId) {
return new Promises((resolve, reject) => {
likeDislikeDao.findDislikeByPostIdAndUserId(postId, userId).
then((data) => {
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
function saveLike(like) {
console.log(like);
return new Promises((resolve, reject) => {
console.log(data);
likeDislikeDao.findLikeByPostIdAndUserId(like.postId, like.userId).
then((data) => {
if (!data) {
likeDislikeDao.findDislikeByPostIdAndUserId(like.postId, like.userId).
then((dislikeData) => {
if (!dislikeData) {
likeDislikeDao.saveLike(like).
then((data) => {
resolve(data);
});
}
else {
likeDislikeDao.deleteDislike(dislikeData._id)
.then((data) => {
likeDislikeDao.saveLike(like).
then((data) => {
resolve(data);
});
});
}
});
}
else {
likeDislikeDao.deleteLike(data._id)
.then((data) => {
//likeDislikeDao.saveLike(like).
// then((data) => {
// resolve(data);
// });
resolve(data);
});
}
})
.catch((error) => {
reject(error);
});;
});
}
Here are my individual test cases.
describe('saveLike', function () {
it('should add a like', function () {
var stub = sinontwo.stub(likeDislikeDao, 'saveLike');
stub.withArgs(newLike).callsFake(() => {
return Promise.resolve(newLike);
});
var stubtwo = sinontwo.stub(likeDislikeDao, 'saveDislike');
stubtwo.withArgs(newDislike).callsFake(() => {
return Promise.resolve(newDislike);
});
const stubthree = sinontwo.stub(likeDislikeDao, 'findLikeByPostIdAndUserId');
stubthree.callsFake(() => {
return Promise.resolve(like);
});
const stubfour = sinontwo.stub(likeDislikeDao, 'findDislikeByPostIdAndUserId');
stubfour.callsFake(() => {
return Promise.resolve(dislike);
});
likeDislikeService.saveLike(newLike).then(response => {
console.log('1 -> ');
console.log(response);
assert.length(response, 1);
});
stub.withArgs(like).callsFake(() => {
return Promise.reject('');
});
stubtwo.withArgs(dislike).callsFake(() => {
return Promise.reject('');
});
likeDislikeService.saveLike(like).then(response => {
console.log('2 -> ');
console.log(response);
assert.lengthOf(response, 1);
}).then((err) => {
console.log(err);
assert.isDefined(err);
});
});
});
describe('findLikeByPostIdAndUserId()', function () {
it('should find likes by post id and user id', function () {
likeDislikeService.findLikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
});
likeDislikeService.findLikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
}).catch((err) => {
//console.log(err);
assert.isDefined(err);
});
})
});
describe('findDislikeByPostIdAndUserId()', function () {
it('should find dislikes by post id and user id', function () {
likeDislikeService.findDislikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
});
likeDislikeService.findDislikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
}).catch((err) => {
console.log(err);
assert.isDefined(err);
});
})
});
Problem is, I am not getting the "reject" part of the find-methods in the coverage. Also, the saveLike method is not being covered properly apart from the 'then' lines.

How to return data after resolving multiple promises simultaneously?

Here I've created a data Object
const data = new Object();
Then I'm calling multiple API's parallelly
datamuse.request(`words?ml=${text}`)
.then((list) => {
data.ml = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?sp=${text}`)
.then((list) => {
data.sp = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?rel_trg=${text}`)
.then((list) => {
data.rel = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`sug?s=${text}`)
.then((list) => {
data.sug = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?sl=${text}`)
.then((list) => {
data.sl = list;
})
.catch((error) => {
console.log(error);
});
And finally returning the data:
return data;
And the data is returned as undefined.
I know I'm performing the asynchronous operations simultaneously.
But I don't want to use function generator in this case because it's too slow.
Can anyone help me out to get those values in the data and then return it?
Something like
Promise.all([
datamuse.request(`words?ml=${text}`),
datamuse.request(`words?sp=${text}`),
datamuse.request(`words?rel_trg=${text}`),
datamuse.request(`sug?s=${text}`),
datamuse.request(`words?sl=${text}`),
]).then(([
ml,
sp,
rel,
s,
sl
]) => {
const data = {
ml,
sp,
rel,
s,
sl,
};
}).catch((err) => {
// Deal with error
});
Or even better you gotta add something for sug and words difference, I let you figure it out :)
const elems = [
'ml',
'sp',
'rel_trg',
's',
'sl',
];
Promise.all(elems.map(x => datamuse.request(`words?${x}=${text}`))
.then((rets) => {
const data = elems.reduce((tmp, x, xi) => ({
...tmp,
[x]: rets[xi];
}), {});
}).catch((err) => {
// Deal with error
});
Ok here is one possible soluce for your words and sug problem
const elems = [{
acronym: 'ml',
req: 'words',
}, {
acronym: 'sp',
req: 'words',
}, {
acronym: 'rel_trg',
req: 'words',
}, {
acronym: 's',
req: 'sug',
}, {
acronym: 'sl',
req: 'words',
}];
Promise.all(elems.map(({
acronym,
req,
}) => datamuse.request(`${req}?${acronym}=${text}`))
.then((rets) => {
const data = elems.reduce((tmp, {
acronym,
}, xi) => ({
...tmp,
[acronym]: rets[xi];
}), {});
}).catch((err) => {
// Deal with error
});
Promise.resolve is the way to go here.
In addition, I would suggest separating the logic from the data.
function getResult(where) {
const result = {}
return Promise.all(
Object
.entries(where)
.map(([key, path]) => datamuse.request(path).then(list => result[key] = list))
)
.then(() => result)
}
function createMapForText(text) {
return {
ml: `words?ml=${text}`,
sp: `words?sp=${text}`,
rel: `words?rel_trg=${text}`,
sug: `sug?s=${text}`,
sl: `words?sl=${text}`
}
}
// ----- Testing -----
// Fake `datamuse` for testing purposes only
const datamuse = {
request: (path) => Promise.resolve('hello')
}
getResult(
createMapForText('hello')
).then(console.log.bind(console))

"n" number of API calls in the sequentially order in the Node.js?

How to call "n" number of API calls in the sequentially order(each API response as input parameter for next API call) in the Node.js?
Please, review below example of promise in which you may find that it will execute synchronously as your requirement.
let firstPromise = (() => {
return new Promise((resolve) => {
resolve("first promise");
});
});
let secondPromise = (() => {
return new Promise((resolve) => {
resolve("second promise");
});
});
let thirdPromise = (() => {
return new Promise((resolve) => {
resolve("third promise");
});
});
let fourthPromise = (() => {
return new Promise((resolve) => {
resolve("fourth promise");
});
});
firstPromise()
.then((res) => {
console.log(res);
return secondPromise();
})
.then((res) => {
console.log(res);
return thirdPromise();
})
.then((res) => {
console.log(res);
return fourthPromise();
})
.then((res) => {
console.log(res);
})
.catch((err)=>{
throw new Error(err);
});

Resources