How to wait until base64 encoding is finished - javascript

I'm trying to put image file encoded into base64, put it in an object(responseData), and res.json(responseData).
The problem is that response is sent before base64 encoding is finished.
How can I delay sending response until encoding is finished.
P.S I'm not sure if sending an image in a json file is the right way.
router.get("/", async (req, res) => {
let responseData;
await Story.find().then(storiesData => {
responseData = [...storiesData];
storiesData.map((storyData, storyIndex) => {
responseData[storyIndex].imageFiles = [];
gfs.files
.find({ _id: { $in: storyData.image } })
.toArray((err, files) => {
files.map((file, imageIndex) => {
let data = [];
var readstream = gfs.createReadStream({
_id: file._id
});
readstream.on("data", chunk => {
data.push(chunk);
});
readstream.on("end", () => {
imageData = Buffer.concat(data);
responseData[storyIndex].imageFiles[imageIndex] = new Buffer(
imageData
).toString("base64");
});
});
});
});
return new Promise((resolve, reject) => {
resolve();
});
});
res.json(responseData);
});

I think something like this might work
router.get("/", async (req, res) => {
let responseData, storiesData;
storiesData = await Story.find();
responseData = [...storiesData];
const mapResults = storiesData.map((storyData, storyIndex) => {
return new Promise((resolve, reject) => {
responseData[storyIndex].imageFiles = [];
gfs.files
.find({_id: {$in: storyData.image}})
.toArray((err, files) => {
files.map((file, imageIndex) => {
let data = [];
var readstream = gfs.createReadStream({
_id: file._id
});
readstream.on("data", chunk => {
data.push(chunk);
});
readstream.on("end", () => {
imageData = Buffer.concat(data);
responseData[storyIndex].imageFiles[imageIndex] = new Buffer(
imageData
).toString("base64");
resolve(responseData[storyIndex])
});
});
});
})
})
const results = await Promise.all(mapResults);
res.json(results);
});
The map is returning an array of promises, so after each one is done, it sends the responseData, check if final result is like you wanted if not adjust to your needs but the overall idea is there.

Related

Attempting to retrieve JSON data results in [ Promise { <pending> }, Promise { <pending> } ]

I am attempting to retrieve the JSON data stored in the following API:
https://api.hatchways.io/assessment/blog/posts
Using node.js and https requests, I constantly receive an array of [ Promise { }, Promise { } ]. Unfortunately, I can only search by one tag at a time, and I have to retrieve a list of posts that has at least one tag from a list of provided tags, before sorting the posts. My code is below:
const express = require('express');
const app = express();
app.get("/api/ping", (req, res) => {
res.status(200).send("{\"success\": true}");
})
app.get("/api/posts", (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
let posts_array = [];
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
console.log(posts_array);
})
app.listen(3000, () => console.log("Listening on port 3000..."));
function getPost(tag) {
const https = require('https');
return new Promise( (resolve, reject) => {
const options = {
hostname: 'api.hatchways.io',
path: `/assessment/blog/posts?tag=${tag}`
}
let body = [];
const req = https.request(options, res => {
res.on('data', data => {
body.push(data);
});
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch (error) {
reject(error);
}
resolve(body);
});
});
req.on('error', error => {
reject(error);
});
req.end();
}).then(function(data) { return data; }, function(error) { console.log(error) });
}
the getPost method is returning a promise, just do this:
app.get("/api/posts", async (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
const promises = [];
tag_array.forEach(tag => {
promises.push(getPost(tag))
});
posts_array = await Promise.all(promises)
console.log(posts_array)
})
Just wait for all promises to resolve using the await keyword.
app.get("/api/posts", async (req, res) => { // Add the async keyword
//... Skipped some code for concision
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
await Promise.all(posts_array) // Add this to await all promises to resolve
console.log(posts_array);
})

How to mock https.get with Jest

I have a function that calls https.get inside a promise which I want to test with Jest.
The function is like this:
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if( chunk ) {
chunks.push(JSON.parse(chunk));
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
const data = doSomething(chunks);
resolve(data)
});
});
})
}
I want to test that when the function resolves on "end" and rejects on "error";
Currently I have a test like this but because .on("end") doesn't get called, the promise never resolves.
describe("request", () => {
it("Should resolve", async (done) => {
const response = await request("my-url");
expect(response).toEqual("some-data")
})
})
How can I mock events like .on("end") to be called and ensure the promise resolves?
You can do something like this.
// ./request.test.js
jest.mock('https', () => ({
methodToMock: {}
}));
const Stream = require('stream');
const request = require("./request");
const httpsMock = require("https");
describe("request", () => {
it("Should resolve", async () => {
var streamStream = new Stream()
httpsMock.get = jest.fn().mockImplementation((url, cb) => {
cb(streamStream)
streamStream.emit('data', 'some');
streamStream.emit('data', '-');
streamStream.emit('data', 'data');
streamStream.emit('end'); // this will trigger the promise resolve
})
const response = await request("my-url");
expect(response).toEqual("some-data");
})
})
const https = require("https");
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if (chunk) {
// chunks.push(JSON.parse(chunk));
chunks.push(chunk);
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
// const data = doSomething(chunks);
const data = chunks.join('');
resolve(data)
});
});
})
}
module.exports = request;
Note that jest.mock('https', ...) need to be called before const request = require("./request"); if you want https to be mocked.

Understanding HTTP function inside Alexa Skill-- JavaScript

I am currently learning how to connect my Amazon Lambda function (in js) to an API. I found the following code which works but I am new to javascript and APIs in general and am not sure what it is doing. Could someone explain to me what this function does and how it works? Thanks!
function httpGet() {
return new Promise(((resolve, reject) => {
var options = {
host: 'api.icndb.com',
port: 443,
path: '/jokes/random',
method: 'GET',
};
const request = https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
}
Here the response object is a node.js stream, a 'push' stream in particular. (This article does a good job of explaining push/pull streams).
const request = https.request(options, (response) => {
// Your request has been successfully made and you are
// handed a response object which is a stream, which will emit
// a 'data' event when some data is available.
response.setEncoding('utf8');
let returnData = '';
// A chunk of data has been pushed by the stream,
// append it to the final response
response.on('data', (chunk) => {
returnData += chunk;
});
// All the data has been pushed by the stream.
// 'returnData' has all the response data. Resolve the
// promise with the data.
response.on('end', () => {
resolve(JSON.parse(returnData));
});
// Stream has thrown an error.
// Reject the promise
response.on('error', (error) => {
reject(error);
});
});
request.end();
I found a better solution. Create a function to getRemoteData and/or postRemoteData.
getRemoteData (url) {
return new Promise((resolve, reject) => {
axios
.get(url)
.then(res => {
console.log(res.data);
resolve(res.data);
})
.catch(error => {
console.error(error);
});
})
},
postRemoteData (url, body) {
return new Promise((resolve, reject) => {
axios
.post(url, body)
.then(res => {
console.log(res.data);
resolve(res.data);
})
.catch(error => {
console.error(error);
});
})
},
Then use these functions in your IntentsHandlers.
//Get
const url = "https://anydir.com"
let speakOutput = "No data";
await logic.getRemoteData(url)
.then((data) => {
speakOutput = data.results.toString();
})
//Post
const url = "https://anydir.com"
let speakOutput = "No data";
await logic.getRemoteData(url, {param1:valueParam1,param2:valueParam2})
.then((data) => {
speakOutput = data.results.toString();
})
Example.
const NumberCharactersIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'NumberCharactersIntent';
},
async handle(handlerInput) {
const url = "https://rickandmortyapi.com/api/character"
let speakOutput = "No data";
await logic.getRemoteData(url)
.then((data) => {
speakOutput = `There are ${data.results.length.toString()} in the Rick and Morty serie`
})
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};

Promise returns wrong value

In my code I try to assign a value to json variable to return it after (because I can't return it from the anon. function).
As my function is async, because it sends requests (maybe someone knows how to make it sync? I didn't plan to make it asynchronous), I've added await before the request (https.get).
I've been trying to get value from the Promise, but it's always undefined, even though I've awaited the async function.
Here's a code:
async function get_users() {
const https = require('https');
var token = '...';
var json = undefined;
await https.get('...', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
json = JSON.parse(data)['response']['items'];
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
return json;
}
get_users().then(function(result) {
console.log(result);
});
Return a Promise and resolve it, when the end event is called, otherwise reject it in case of an error occurred:
async function get_users() {
const https = require('https');
const token = '...';
return new Promise((resolve, reject) => {
https.get('...', resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
let json;
try {
json = JSON.parse(data)['response']['items'];
} catch (e) {
reject(e);
};
resolve(json);
});
}).on("error", err => reject(err));
});
}
get_users().then(result => console.log(result));
Please refer my below code.I had issues with getting responses from Promises too.But i finally got it to work.Here's the code:
var output;
var rp = require('request-promise-native');
var myJSONObject = {
"inputs": [{
"name": "<name>",
"value": < value >
}]
};
var orchName = 'TEST05';
postData = JSON.stringify(myJSONObject);
return networkCall(postData, orchName).then((response) => {
console.log('response is' + response)
}).catch((response) => {
console.log(`ERROR: ` + response);
});
function networkCall(postData, orchName) {
return new Promise((resolve, reject) => {
var options = {
method: 'post',
uri: '<URL>',
body: postData,
auth: {
'user': 'usr',
'pass': 'pwd'
},
json: true
};
return rp(options)
.then(body => {
var response = body;
resolve(response);
})
.catch(err => {
console.log('FAILED' + err);
reject(err);
});
});
}
This way your code can run in Synchronous Flow.If the return value is undefined,then,what might have probably happened is that the calling function would have finished executing even before the called function returns its response.But the above approach would work just fine.

Asynchronously check folder contents and return file paths

I want to write a script in Node, that it checks if in given array of urls there's files and then retrieve it. I would like to make it asynchronous.
As far as I was able to develop a proper function:
const fs = require('fs-extra');
function getFiles(pathArr = []) {
return new Promise((resolve, reject) => {
let filesArr = pathArr.filter(obj => {
return fs.lstatSync(obj, (err, stat) => {
if (err) {
return false;
}
return stat.isFile();
});
});
resolve(filesArr);
});
}
But it uses fs.lstatSync and I want to use fs.lstat to be able to use it as a async method (with .then() usage). pathArr argument is an array of urls (strings) which I want to check if they're a file or folder.
Please help!
How about this
const fs = require('fs-extra');
function getFiles(pathArr = []) {
var promises = [];
return new Promise((resolve, reject) => {
let filesArr = pathArr.filter(obj => {
promises.push(fileStat(obj));
});
Promise.all(promises).then(function (result) {
resolve(result);
});
});
}
function fileStat(obj) {
return new Promise((resolve, reject) => {
return fs.lstat(obj, (err, stat) => {
if (err) {
reject(err);
}
resolve(stat.isFile())
});
});
}
You can use any of async.series, async.map, async.each. Which takes array as parameter.Refer following example which calculates total files size asynchronously.
let fs = require("fs");
let async = require('async');
let paths = ['./demo1.txt', './demo2.txt', './demo3.txt'];
let totalSize = 0;
let calcSize = function () {
async.each(paths, function iterator(path, next) {
let fileName = path.split("/");
fs.stat(path, function (err, stat) {
totalSize += stat.size;
next(null);
});
},function(){ console.log("totalSize : "+totalSize)})
}
calcSize();

Categories