How do I copy a node(object) in Firebase to another node? - javascript

I am having a node called Events in Firebase. It consists of child objects like: address, description, longitude, latitude. Before a user deletes an event node I want to copy it to the same database to a node called eventsDeleted.
This is the code for deleting the node:
removeEvent(eventId, groupId) {
return new Promise((resolve, reject)=> {
this.eventRef.child(groupId).child(eventId).remove();
resolve();
});
}
This is the code for creating the node:
addEvent(data:any) {
console.log('Form data', data.group);
let localEventRef = firebase.database().ref('events').child(data.group.split(',')[1]);
let storageRef = firebase.storage().ref();
let file = data.image;
let uploadTask = storageRef.child('eventImages/' + UuidGenerator.generateUUID()).put(file);
uploadTask.on('state_changed', function (snapshot) {
}, function (error) {
// Handle unsuccessful uploads
console.error(error);
}, function () {
// Handle successful uploads on complete
let downloadURL = uploadTask.snapshot.downloadURL;
let keyOfNewEvent = localEventRef.push(
new Event(
null,
firebase.app().auth().currentUser.uid,
data.description,
data.location.address,
0
)
).key;
localEventRef.child(keyOfNewEvent).update({eventId: keyOfNewEvent});
});
}
Never mind the code for uploading an image. I just need a way to copy that entire node if possible then paste it somewhere in the database. Thanks in advance.

When the user clicks delete, make sure to get the object that's being deleted, if you were to query your database for that object you can use the .once to retrieve the object otherwise you can just jump to the removeEvent function directly.
localEventRef.child(keyOfEvent).once("value", function(snapshot) {
//one data is returned, you can then call the removeEvent fn
let eventToBeRemoved = snapshot.val();
//assuming you have the eventid, groupid then.
removeEvent(eventId, groupId, eventToBeRemoved);
});
removeEvent(eventId, groupId, eventObjectToBeRemoved) {
//firebase 3.x comes with a promise method
firebase.database().ref('eventsDeleted/' + groupId + '/' + eventId )
.set({...eventObjectToBeRemoved})
.then(function () {
eventRef.child(groupId).child(eventId).remove();//you can now remove
})
.catch(function (error) {
console.error("error occured trying to add to deletedEvents", error);
});
});
}

Related

Update same state variable with multiple async function?

I have a function that stores videos in firebase storage. In the function, I have a snapshot listener that listens to the upload progress and calls another function, "UploadingProgress" to update a state variable that holds the "percentage". This is the function below:
feedUploadProgress: async (Data, UploadingProgress, mediaIndex) => {
try {
let url = null;
var uploadTask = Data.videoRef.put(Data.video, Data.metadata); // not await as we want to async put files
await new Promise((resolve, reject) => {
uploadTask.on(
"state_changed", // or 'state_changed'
(snapshot) => {
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
UploadingProgress(
true,
mediaIndex,
snapshot.bytesTransferred,
snapshot.totalBytes
);
},
(error) => {
console.log(error.code);
url = false;
reject(error.code);
switch (error.code) {
case "storage/unauthorized":
// User doesn't have permission to access the object
break;
case "storage/canceled":
// User canceled the upload
break;
case "storage/unknown":
// Unknown error occurred, inspect error.serverResponse
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
url = downloadURL;
UploadingProgress(false, null, 0, 10000000);
//resolve(downloadURL);
});
}
);
});
return url;
} catch (error) {
console.log("Error #uploadProgress: ", error);
}
},
And UploadProgress:
const showUploading = (status, mediaIndex, bytesTransferred, totalBytes) => {
if (status) {
//if the status (which is snapshot listner status) is true, then will show upload progress
if (!isUploading) {
setIsUploading(true);
let tempMediaPercentages = new Map();
tempMediaPercentages.set(mediaIndex, bytesTransferred / totalBytes);
setMediaPercentages(tempMediaPercentages);
console.log(mediaIndex, tempMediaPercentages);
}
} else {
setIsUploading(false);
setUploadPercentage(0);
setMediaPercentages(new Map()); // resets the media percentages
}
};
The state variable I'm trying to manipulate is "mediaPercentages", a map but in the scenario where I have 2 async calls on "feedUploadProgress" the state changes aren't made but instead of 1 state being updated by each of these async calls, what I get instead is seemingly 2 different states that aren't aware of the changes made by each separate function call.
In this screen shot below I console log the entire state which should contain 2 key-value pairs, 0 & 1, but as the map is getting changed it makes seemingly independent changes rather than having 1 map containing 2 key-value pairs together.

Making a distinction between file not present and access denied while accessing s3 object via Javascript

I have inherited the following code. This is part of CICD pipeline. It tries to get an object called "changes" from a bucket and does something with it. If it is able to grab the object, it sends a success message back to pipeline. If it fails to grab the file for whatever reason, it sends a failure message back to codepipeline.
This "changes" file is made in previous step of the codepipeline. However, sometimes it is valid for this file NOT to exist (i.e. when there IS no change).
Currently, the following code makes no distinction if file simply does not exist OR some reason code failed to get it (access denied etc.)
Desired:
I would like to send a success message back to codepipeline if file is simply not there.
If there is access issue , then the current outcome of "failure' would still be valid.
Any help is greatly appreciated. Unfortunately I am not good enough with Javascript to have any ideas to try.
RELEVANT PARTS OF THE CODE
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const lambda = new AWS.Lambda();
const codePipeline = new AWS.CodePipeline();
// GET THESE FROM ENV Variables
const {
API_SOURCE_S3_BUCKET: s3Bucket,
ENV: env
} = process.env;
const jobSuccess = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobSuccessResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
const jobFailure = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobFailureResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
// MAIN CALLER FUNCTION. STARTING POINT
exports.handler = async (event, context, callback) => {
try {
// WHAT IS IN changes file in S3
let changesFile = await getObject(s3, s3Bucket, `lambda/${version}/changes`);
let changes = changesFile.trim().split("\n");
console.log("List of Changes");
console.log(changes);
let params = { jobId };
let jobSuccessResponse = await jobSuccess(codePipeline, params);
context.succeed("Job Success");
}
catch (exception) {
let message = "Job Failure (General)";
let failureParams = {
jobId,
failureDetails: {
message: JSON.stringify(message),
type: "JobFailed",
externalExecutionId: context.invokeid
}
};
let jobFailureResponse = await jobFailure(codePipeline, failureParams);
console.log(message, exception);
context.fail(`${message}: ${exception}`);
}
};
S3 should return an error code in the exception:
The ones you care about are below:
AccessDenied - Access Denied
NoSuchKey - The specified key does not exist.
So in your catch block you should be able to validate exception.code to check if it matches one of these 2.

File metadata is not getting updated in Firestore Storage

I have created a Cloud Function that trigger on any new file upload in Firebase Storage. Once successful upload function will update its metadata, but even though setting new metadata with 'setMetadata()' is not getting applied. There is no error during the process and but on checking for updated metadata, the new one is not reflecting.
exports.onImageUpload = functions.storage.object().onFinalize(async (object) => {
const storageRef = admin.storage().bucket(object.bucket);
var metadata = {
'uploader': 'unknown'
}
await storageRef.file(object.name).setMetadata(metadata).then(function(data) {
console.log('Success');
console.log(data);
return;
}).catch(function(error) {
console.log(error);
return ;
});
return;
});
There is no error, and on Cloud Function log its printing 'Success' message. Also "metageneration: '2'" property also got updated, which means it should have updated metadata with new values, but it didn't.
The problem comes from the fact that if you want to set custom key/value pairs they must be in the metadata key of the object you pass to the setMetadata() method, i.e. the metadata object in your case. This is explained in the API Reference Documentation for node.js.
So the following will work:
exports.onImageUpload = functions.storage.object().onFinalize(async (object) => {
const storageRef = admin.storage().bucket(object.bucket);
var metadata = {
metadata: {
'uploader': 'unknown'
}
}
try {
const setFileMetadataResponse = await storageRef.file(object.name).setMetadata(metadata);
console.log('Success');
console.log(setFileMetadataResponse[0]);
return null;
} catch (error) {
console.log(error);
return null;
}
});

Send messages to specific devices using firebase functions

I tried sending a specific message to a client device using node js and firebase functions. But when I tried executing the function, it came back with an error saying:
Error. Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
The image is shown below.
I was guessing it's from my JS code. So I am posting that too. What I am actually do is retrieving a data from a specific node to be used when a totally different node is being written. So I am gonna post the JS code before the database screenshots.
exports.sendNotification8 = functions.database.ref('/Users/{user_id}/Notifications/')
.onWrite(( change,context) =>{
var user_id = context.params.user_id;
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var device_token = admin.database().ref('/Users/{user_id}/device_token').once('value');
return device_token.then(result => {
var token_id = result.val();
var str = eventSnapshot.from + " : " + eventSnapshot.message;
console.log(eventSnapshot.from);
var payload = {
data: {
name: str,
title: eventSnapshot.from,
click_action: "Chats"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToDevice(token_id, payload).then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});
And below is my database screenshots...
So that's how I am retrieving the device_token node. From the user that had the newest data written to his/her notifications node. Please help. What am I doing wrong?
Wow. This has been torture. But it finally worked. I got something like this.
exports.sendNotification8 = functions.database.ref('/Users/{user_id}/Notifications/{notifications_id}')
.onWrite((change,context) =>{
var user_id = context.params.user_id;
console.log(user_id);
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var device_token = admin.database().ref('/Users/'+user_id+'/device_token').once('value');
return device_token.then(result => {
var token_id = result.val();
console.log(token_id);
var str = eventSnapshot.message;
console.log(eventSnapshot.from);
var payload = {
data: {
name: str,
title: eventSnapshot.from,
click_action: "Chats"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToDevice(token_id, payload).then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});

Save to database sequentially with NodeJS

I am trying to write a back-end in NodeJS for a newsreader app. The idea is to check a number of RSS feeds every few minutes, save new items to a database, and emit an event to connected users when a new item is added to the database.
I am having trouble writing a module which saves to the database. The desired behaviour is as follows:
Take the parsed RSS feeds, which are ordered in an array from newest to oldest, as input
For each item, starting with the newest, attempt to save it in the database
If the save was successful, log 'Save successful' to the console and attempt the next item
If the save fails, because the item already exists in the database, stop running the module.
The database model is already configured so the database does reject it when I try to save an item which already exists. However, the code below never logs the successful saves, and if I drop the database, it only saves the first document and then gives me a duplicate key error.
Any idea what I am doing wrong?
// Module
var { mongoose } = require('../db/mongoose');
var { Item } = require('../models/item');
var { scrape } = require('./scrape')
var { parse } = require('./parse')
var updateNewsDatabase = function() {
return new Promise((resolve, reject) => {
console.log('Scraping now')
scrape().then((rssFeeds) => {
var output = parse(rssFeeds);
saveEachNewsItem(output)
.catch((e) => {
console.log('Error:', e);
resolve()
})
})
});
}
async function saveEachNewsItem(newsItems) {
for (let item of newsItems) {
console.log('Attempting to save document')
var itemToSave = new Item(item);
await itemToSave.save()
.then((err, docs) => {
if (docs) {
console.log('Saved document to database')
}
if (err) {
throw new Error(err)
}
});
}
}
module.exports = { updateNewsDatabase }
In this part of your code, you are throwing exception and it makes it stop the for loop.
await itemToSave.save()
.then((err, docs) => {
if (docs) {
console.log('Saved document to database')
}
if (err) {
throw new Error(err)
}
});

Categories