Merge PDFs collected from storage in firebase cloud function - javascript

I'm new to node.js and backend coding. I wrote an application (TMS) using vuejs. I need to create new pdf and merge 3 others (created on frontend and uploaded to firebase storage) inside of it. Here's my code:
exports.testCloudFunc = functions.storage.object().onFinalize(async object => {
const filePath = object.name;
const { Logging } = require('#google-cloud/logging');
console.log(`Logged: FILEPATH: ${filePath}`);
const id = filePath.split('/');
console.log(`Logged: ID: ${id[0]}/${id[1]}`);
const bucket = object.bucket;
console.log(`Logged: BUCKET: ${object.bucket}`);
async function listFilesByPrefix() {
const options = {
prefix: id[0] + '/' + id[1]
};
in
const endFiles = files.filter(el => {
return (
el.name === id[0] + '/' + id[1] + '/' + 'invoiceReport.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'POD.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'rateConfirmation.pdf'
);
});
for (let i = 0; i < endFiles.length; i++) {
console.log(endFiles[i].name);
}
}
listFilesByPrefix().catch(console.error);
});
Can you give me an advice how to do this (what lib to use and some example code maybe)? Also I wonder what type of data is when I get files from storage and store it in array or variable. Do Can I avoid saving it to local temp folder for futher manipulations like merge? Or I can just merge it by giving a link to endfiles[index]? My project is 99% finished. This is the only thing left to do. Many thanks in advance!

Related

Using local storage to sync data across pages

I'm very new to JS and I'm trying to sync the listing data by using local storage example below:
https://imgur.com/lN1x1TH
Song 1 -> Song 1 Detail page
Song 2 -> Song 2 Detail page
Song 3 -> Song 3 Detail page
There will be only one detail page associated with the 3 listings.
Anyway to achieve this without creating multiple pages for the individual detail listing?
index.js
fetch('x')
.then(function(res) {
return res.json();
})
.then(function(data) {
var Data = JSON.stringify(data);
if (localStorage.getItem("songStoreData") === null || typeof localStorage.getItem("songStoreData") === "undefined") {
localStorage.setItem("songStoreData", Data);
}
//get the stored data
var songJSON = localStorage.getItem("songStoreData");
//parse the data to JSON
var Obj = JSON.parse(songJSON);
//for debugging
console.log(Obj);
for (i = 0; i < Obj.songs.length; i++) {
homeList.innerHTML +=
'<li class="li_product_"' + i + '"><div class="li_product_image"><img src="' + Obj.songs[i].image + '"></div><div class="li_product_name">' + Obj.songs[i].name + ', ' + Obj.songs[i].artist + '<br><span class="li_product_duration">' + Obj.songs[i].duration + ' minutes</span></div></div></li>';
}
})
detail.js
var songJSON=localStorage.getItem("songStoreData");
var Obj=JSON.parse(songJSON);
console.log(Obj);
function addDetails(){
document.getElementById('span_header').innerHTML = '<img id="btn_product_details_back" src="img/back_white.png">' + Obj.songs.artist + '';
document.getElementById('div_product_details_img').innerHTML = '<img src="' + Obj.songs.image + '">';
document.getElementById('data_name').innerHTML = Obj.songs.name;
document.getElementById('data_genre').innerHTML = Obj.songs.genre;
document.getElementById('data_release').innerHTML = Obj.songs.release;
document.getElementById('data_duration').innerHTML = Obj.songs.duration;
var Data=JSON.stringify(Obj);
//store the string format data in local storage
localStorage.setItem("songStoreData", Data);
//window.location='index.html';
return false;
}
addDetails();

When I add a new element to my database the whole things stops to work

I am still trying to build my website where people can text each other, send photos etc.
The chat thing works really well until I wanna add the functionality checking whether the second user in the chat is typing.
Here is my code without the typing thing, this works really well ;)
firebase.initializeApp(firebaseConfig);
const db = firebase.database();
// temporary user and receiver's names
const username = prompt("Nickname:");
const receiver = prompt("Receiver's name:");
// sending a message
document.getElementById("send-message").addEventListener("submit", postChat);
function postChat(e)
{
e.preventDefault();
const timestamp = Date.now();
const chatTxt = document.getElementById("chat-txt");
const message = chatTxt.value;
chatTxt.value = "";
db.ref("messages/" + username + "/" + receiver + "/" + timestamp).set({
usr: username,
msg: message,
});
db.ref("messages/" + receiver + "/" + username + "/" + timestamp).set({
usr: username,
msg: message,
});
}
// printing the message
const fetchChat = db.ref("messages/" + username + "/" + receiver + "/");
fetchChat.on("child_added", function (snapshot)
{
const messages = snapshot.val();
const msg = "<li>" + messages.usr + " : " + messages.msg + "</li>";
document.getElementById("messages").innerHTML += msg;
});
The problem appears when I want to check if the second user is typing. When I am adding the code that's below messages just stop to work. There is a random null in the database as a new user who sent a message to another null. The chat between users also stops to work, users don't see same messages, sometimes can't see any of them and always "undefined" types "undefined" when I refresh the website.
At least it correctly shows when someone is typing, but the rest of the functionalities (which used to work) just don't work anymore.
Here is the code of the typing thing, I also tried to check whether the username and receiver's name aren't null but it didn't reallly help.
// showing whether the receiver is typing
function sendTyping(tmp)
{
if(tmp)
{
db.ref("messages/" + username + "/" + receiver).set({
tpg: "yes"
});
}
else
{
db.ref("messages/" + username + "/" + receiver).set({
tpg: "no"
});
}
}
var searchTimeout;
document.getElementById("chat-txt").onkeydown = function() {
if (searchTimeout != undefined)
clearTimeout(searchTimeout);
searchTimeout = setTimeout(callServerScript, 1500);
sendTyping(true);
}
function callServerScript() {
sendTyping(false);
}
let areTheyTyping = db.ref("messages/" + receiver + "/" + username);
areTheyTyping.on("value", function(snapshot) {
const typing = snapshot.val();
const status = typing.tpg;
if(status == "yes")
document.getElementById("writing").innerHTML = "typing...";
else
document.getElementById("writing").innerHTML = "";
});
I mostly wrote this by myself, I will appreciate any kind of help, just please use a straightforward language so I can easily understand the explanation of the problem, I am kind of new.
The function
const fetchChat = db.ref("messages/" + username + "/" + receiver + "/");
fetchChat.on("child_added", function (snapshot)
{
const messages = snapshot.val();
const msg = "<li>" + messages.usr + " : " + messages.msg + "</li>";
document.getElementById("messages").innerHTML += msg;
});
It fetches the chat whenever a child is added to the directory of your messages, so when you store the typing status in "messages/" + username + "/" + receiver + "/" the function .on knows that you've changed/added something, therefore posting a message. It's undefined because the message is in fact empty. You should create a new directory where you store the areTheyTyping status, something like "status/" + username + "/" + receiver + "/" Hope I helped.
My experience showed that firestore has problem with undefined values. You can try adding a default value for undefined or null usernames. Maybe you can add zero (0) as the default value for this matter.
Change the path db.ref("messages/" + receiver + "/" + username) to db.ref("messages/" + username + "/" + receiver) at areTheyTyping. See if it works
function sendTyping(tmp)
{
if(tmp) {
db.ref("messages/" + username + "/" + receiver).set({
tpg: "yes"
});
}
else {
db.ref("messages/" + username + "/" + receiver).set({
tpg: "no"
});
}
}
var searchTimeout;
document.getElementById("chat-txt").onkeydown = function() {
if (searchTimeout !== undefined)
clearTimeout(searchTimeout);
searchTimeout = setTimeout(callServerScript, 1500);
sendTyping(true);
}
function callServerScript() {
sendTyping(false);
}
let areTheyTyping = db.ref("messages/" + username + "/" + receiver);
areTheyTyping.on("value", function(snapshot) {
const typing = snapshot.val();
const status = typing.tpg;
if(status === "yes")
document.getElementById("writing").innerHTML = "typing...";
else
document.getElementById("writing").innerHTML = "";
});

Running parallel execution of fuctions using Promise all

I have a requirement to execute several parallel functions
First functions:
Database get operation ie find inventory details from database
Second functions:
Process db results and save the result as a file
I am using two promise all, back to back to execute the above functions, I don't feel like this is the correct way to do this. Is there a
better way of handling these function calls. I am getting the result as per below code but want to know any other way.
Doing it following way:
let allQuery = {
sql: "SELECT * from inventory",
};
let inventoryQuery = {
sql: "SELECT * from inventory where inventory='1'",
};
let nearbyQuery = {
sql: "SELECT * from inventory where inventory='2",
};
let firstPromises = [dbService.fetch(allQuery),
dbService.fetch(inventoryQuery),
dbService.fetch(nearbyQuery)
];
Promise.all(firstPromises)
.then((values) => {
let all = values[0];
let inventory = values[1];
let nearby = values[2];
let fileKey1 = folderName + '/' + all.QueryExecutionId + '.csv';
let fileName1 = all.QueryExecutionId + '.csv';
let fileKey2 = folderName + '/' + inventory.QueryExecutionId + '.csv';
let fileName2 = inventory.QueryExecutionId + '.csv';
let fileKey3 = folderName + '/' + nearby.QueryExecutionId + '.csv';
let fileName3 = nearby.QueryExecutionId + '.csv';
let secondPromises = [s3Service.s3StreamDownload(bucketName, fileKey1, fileName1),
s3Service.s3StreamDownload(bucketName, fileKey2, fileName2),
s3Service.s3StreamDownload(bucketName, fileKey3, fileName3)
];
Promise.all(secondPromises)
.then((values) => {
console.log('Do later operation');
}).catch((error) => {
debug(`Error in promises ${error}`);
});
}).catch((error) => {
debug(`Error in promises ${error}`);
});
I think it can be more readable to extract the inner function and then chain them together:
Promise.all(firstPromises)
.then(transformToSecondPromises)
.then(Promise.all)
.then(values => {/* do later operation */})
.catch(error => { debug(`Error in promises ${error}`) })
function transformToSecondPromises ([all, inventory, nearby]) {
const fileKey1 = folderName + '/' + all.QueryExecutionId + '.csv';
const fileName1 = all.QueryExecutionId + '.csv';
const fileKey2 = folderName + '/' + inventory.QueryExecutionId + '.csv';
const fileName2 = inventory.QueryExecutionId + '.csv';
const fileKey3 = folderName + '/' + nearby.QueryExecutionId + '.csv';
const fileName3 = nearby.QueryExecutionId + '.csv';
return [
s3Service.s3StreamDownload(bucketName, fileKey1, fileName1),
s3Service.s3StreamDownload(bucketName, fileKey2, fileName2),
s3Service.s3StreamDownload(bucketName, fileKey3, fileName3)
];
}

Firebase getSignedUrl inside loop

I need to get urls of all files in 'bucket/loads/:loadID' path. I'm able to get these files in array called 'files'. Then I filter it (I get endFiles array). And now I just need a new array called url to push all the urls in (getSignedUrl). But I don't know how to do It. I need to get signed urls inside a loop (endFiles.forEach) and push it to urls array or something like that.
exports.testCloudFunc = functions.storage.object().onFinalize(async object => {
const filePath = object.name;
const { Logging } = require('#google-cloud/logging');
console.log(`Logged: FILEPATH: ${filePath}`);
const id = filePath.split('/');
console.log(`Logged: ID: ${id[0]}/${id[1]}`);
const bucket = object.bucket;
console.log(`Logged: BUCKET: ${object.bucket}`);
async function listFilesByPrefix() {
const options = {
prefix: id[0] + '/' + id[1]
};
const [files] = await storage.bucket(bucket).getFiles(options);
const endFiles = files.filter(el => {
return (
el.name === id[0] + '/' + id[1] + '/' + 'invoiceReport.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'POD.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'rateConfirmation.pdf'
);
});
for (let i = 0; i < endFiles.length; i++) {
console.log(endFiles[i].name);
}
}
listFilesByPrefix().catch(console.error);
});
I'm stuck and need help. Any help is highly appreciated.
The getSignedUrl() method is asynchronous and returns a Promise.
Since you want to concurrently execute multiple calls to this method, you need to use Promise.all() as follows:
async function listFilesByPrefix() {
const options = {
prefix: id[0] + '/' + id[1]
};
const [files] = await storage.bucket(bucket).getFiles(options);
const endFiles = files.filter(el => {
return (
el.name === id[0] + '/' + id[1] + '/' + 'invoiceReport.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'POD.pdf' ||
el.name === id[0] + '/' + id[1] + '/' + 'rateConfirmation.pdf'
);
});
const config = {
action: 'read',
expires: '03-17-2025'
};
const promises = [];
for (let i = 0; i < endFiles.length; i++) {
console.log(endFiles[i].name);
promises.push(endFiles[i].getSignedUrl(config));
}
const urlsArray = await Promise.all(promises);
return urlsArray;
}
listFilesByPrefix()
.then(results => {
//results is an array of signed URLs
//It's worth noting that values in the array will be in order of the Promises passed with promises.push()
//do whatever you need, for example:
results.forEach(url => {
//....
});
})

selenium screenshots not saving specified directory

I have my selenium test set up to take screenshots, but they are not saving to the directory which I have specified. Can anybody show me what I am missing?
Here is how I am configuring the screenshots in the test:
function writeScreenshot(data, name) {
var fs = require('fs');
name = name || 'ss.png';
var screenshotPath = mkdirp(configuration.readSettings('screenshotDirectory') + fileNameURL + "/", function(err){});
fs.writeFileSync(screenshotPath + name, data, 'base64');
};
and then I take the screenshot:
driver.takeScreenshot().then(function(data) {
var screenshotFile = os + '_' + osVersion + '_' + browser + '_' + browserVersion + '.png';
writeScreenshot(data, screenshotFile);
});
The screenshots end up being saved instead in the projects root directory and with the file name preceded by 'undefined'. (ex. undefinedWindows_8_chrome_46.png)
It does, however, create the folders shown here: var screenshotPath = mkdirp(configuration.readSettings('screenshotDirectory') + fileNameURL + "/", function(err){});
So why is this happening?
mkdirp() is an async method. That is why you pass a callback. You will need to change your code to something like the following:
function writeScreenshot(data, name) {
var fs = require('fs');
name = name || 'ss.png';
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
mkdirp(screenshotPath, function(err){
if (err) {
// something else happened while creating the dir. You decide what to do
return;
}
// Otherwise (if dir was created)
fs.writeFileSync(screenshotPath + name, data, 'base64');
});
};
mkdirp() function is asynchronous - it creates a directory and returns nothing - this is why you having that leading undefined in the filename.
Save the file in the callback:
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
mkdirp(screenshotPath, function (err) {
if (!err) {
fs.writeFileSync(screenshotPath + name, data, 'base64');
} else {
// handle error
}
});
Or, synchronously create the directory and write to it this way:
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
if (mkdirp.sync(screenshotPath)) {
fs.writeFileSync(screenshotPath + name, data, 'base64');
}

Categories