Adding or deleting data in a file with Cloud Functions - firebase

my idea is to be able to edit files in storage.
This edition consists of adding or deleting file data according to the firebase trigger.
I created a trigger in firebase after obtaining the file with the bucket.file function ("file.txt"). CreateReadStream ()
and I edited the data in the base in the change in the firebase after this I updated the file with the function
bucket.file ("file.txt"). createWriteStream ().
This solution is good when there is 1 trigger, but when there are more than 2 triggers, the data does not keep correctly why the file is overwritten with the data it had before.
Example
this is the content of file.txt
This text is an example
and executed 2 triggers
the 2 activators get the file at the same time and the first trigger adds data and overwrites the file with this message
this text is an example
and this file was edited with the first trigger
and the second activator erases data and overwrites the file with this message
this text
When the triggers are finished, the file has "this text"
but this file must have
this text
and this file was edited with the first trigger
Someone help me.
exports.createData = functions.database.ref('data/{id}/summary/status').onCreate((data, context) => {
let status = data._data;
return Promise.all([ admin.database().ref('data/' + context.params.id + '/summary/entityUrl').once('value', (snapshot) => {
let entityUrl = snapshot.val();
if (isDataValid(status))
return addDataFile(entityUrl) ;
return;
}) ]);
})
function addDataFile(entityUrl){
return Promise.all([ returnFile("txt",() => {
dataFile.splice(dataFile.length - 1, 0, `new data ${entityUrl}`)
updateFileStorage("txt", dataFile.join('\n'));
}) ]);
}
function returnFile(extension, callback) {
let respData = "";
if (dataFile == null ){
return bucket.file(FileUrl + extension).createReadStream()
.on('data', (chunk) => {
respData += chunk;
})
.on('end', () => {
dataFile = respData.split('\n');
callback();
})
.on('error', (error) => {
console.log("Error en lectura")
return returnFile(extension, callback);
})
}
else callback();
return;
}
function updateFileStorage(extension,data, trys ){
trys = typeof trys !== 'undefined' ? trys : 0;
if(trys>6)
return;
var s = new Readable();
s._read = function noop() { };
s.push(data);
s.push(null);
return s.pipe(bucket.file(FileUrl + extension).createWriteStream())
.on('finish', function () {
//console.log("File updated");
return;
})
.on('error', function (err) {
console.log("Error de Escritura");
return setTimeout(() => {
return updateFileStorage(extension, data, trys + 1)
}, 250);
})
}

Related

Cloud Functions bucket.upload() is not running at all

here is what I am trying to do using Firebase:
create a backup file from realtime database
upload to firebase storage
do this every morning
but I am having problem on number 2; after the log of back up file creation success, no other log appears, not even a failed message.
no log after file creation
Even worse is that it sometimes works, which makes me doubtful about the consistency of the functionality.
my code:
var promiseFileCreation = function(fileName, jsonBackup){
console.log("promiseFileCreation starting");
return new Promise(function (resolve, reject){
fs.writeFile('/tmp/'+fileName, jsonBackup, function(fs_err){
if(!fs_err){
resolve("File "+fileName+" creation success");
} else {
reject("File "+fileName+" creation failure: "+fs_err);
}
})
}).catch(function(error){
reject("FileCreation Error");
})
}
var promiseBucketUpload = function(fileName, fileDest){
console.log("promiseBucketUpload starting")
return new Promise(function (resolve, reject){
console.log("promiseBucketUpload promise starting")
bucket.upload('/tmp/'+fileName, { destination: fileDest }, function(upload_err){
if(!upload_err){
resolve("File "+fileName+" upload to "+fileDest+" success");
} else {
reject("File "+fileName+" upload to "+fileDest+" failure: "+upload_err);
}
})
}).catch(function(error){
reject("BucketUpload Error: "+error);
})
}
Promise.all([promiseText, promiseDate, promiseTitle, promiseLikedCount, promiseViewCount, promiseComments]).then(function (values){
var jsonPostObj = {
post: [],
counter: []
}
jsonPostObj.post.push({
date: values[1],
text: values[0],
title: values[2]
})
jsonPostObj.counter.push({
likedCount: values[3],
viewCount: values[4]
})
var jsonCommentsObj = JSON.parse(values[5]);
const jsonArchiveObj = {...jsonPostObj, ...jsonCommentsObj}
var jsonArchive = JSON.stringify(jsonArchiveObj);
const yesterday = getYesterdayDateFull();
var fileName = "archive_"+yesterday;
var fileDest = "history/"+yesterday.substring(0,4)+"/"+yesterday.substring(4,6)+"/"+fileName;
console.log("Archive file name: "+fileName);
console.log("Archive destination: "+fileDest);
promiseFileCreation(fileName, jsonArchive).then(function(resultSuccessFs){
console.log(resultSuccessFs);
// BUCKETUPLOAD here
promiseBucketUpload(fileName, fileDest).then(function(resultSuccessBucket){
console.log(resultSuccessBucket);
return promiseBackupResult(true);
}, function(resultFailureBucket){
console.log(resultFailureBucket);
return promiseBucketResult(false);
})
}, function(resultFailureFs){
console.log(resultFailureFs);
return promiseBackupResult(false);
});
}).catch(function(errPromiseAll){
console.log("Promise.all error: "+errPromiseAll);
return promiseBackupResult(false);
})
}
I removed unnecessary codes, like other promises. The file creation seems to work fine.
Does anyone see why bucket.upload() is not called at all? Thanks in advance.

alexa sdk: can't get persitentAttributes

i'm trying to add persistent attributes to my lambda function.
i created a dynamoDB table and added it to the triggers of my lambda function.
i copied a sample code from github, but when i try to launch the skill i get an error. The console log shows:
{
"errorMessage": "Could not read item (amzn1.ask.account.AGIIYNRXWDLBD6XEPW72QS2BHGXNP7NWYBEWSH2XLSXZP64X3NCYEMVK233VFDWH77ZB6DAK6YJ53SZLNUFVQ56CYOVCILS7QFZI4CIRDWC3PAHS4QG27YUY5PTT6QEIK46YFNTJT54YAKNGOWV2UO66XZACFDQ5SEXKJYOBNFNIZNUXKNTIAAYZG4R5ZU4FMLPDZZN64KLINNA) from table (Spiele): The provided key element does not match the schema",
"errorType": "AskSdk.DynamoDbPersistenceAdapter Error",
"stackTrace": [
"Object.createAskSdkError (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/utils/AskSdkUtils.js:22:17)",
"DynamoDbPersistenceAdapter.<anonymous> (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:123:49)",
"step (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:44:23)",
"Object.throw (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:25:53)",
"rejected (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:17:65)",
"<anonymous>",
"process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
the table contains a primary key "name" and sort key "UserId". is that wrong?
here is my index.js:
const Alexa = require('ask-sdk');
// Define the skill features
let skill;
/**
* If this is the first start of the skill, grab the user's data from Dynamo and
* set the session attributes to the persistent data.
*/
const GetUserDataInterceptor = {
process(handlerInput) {
let attributes = handlerInput.attributesManager.getSessionAttributes();
if (handlerInput.requestEnvelope.request.type === 'LaunchRequest' && !attributes['isInitialized']) {
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((attributes) => {
attributes['isInitialized'] = true;
saveUser(handlerInput, attributes, 'session');
resolve();
})
.catch((error) => {
reject(error);
})
});
}
}
};
function saveUser(handlerInput, attributes, mode) {
if(mode === 'session'){
handlerInput.attributesManager.setSessionAttributes(attributes);
} else if(mode === 'persistent') {
console.info("Saving to Dynamo: ",attributes);
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((persistent) => {
delete attributes['isInitialized'];
handlerInput.attributesManager.setPersistentAttributes(attributes);
resolve(handlerInput.attributesManager.savePersistentAttributes());
})
.catch((error) => {
reject(error);
});
});
}
}
const LaunchHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
console.info("LaunchRequest");
let attributes = handlerInput.attributesManager.getSessionAttributes();
console.info("Test the load: " + attributes['isInitialized']);
attributes['FOO'] = "BAR";
saveUser(handlerInput, attributes, 'persistent');
return handlerInput.responseBuilder
.speak('Hello')
.reprompt('Hello')
.getResponse();
}
}
exports.handler = Alexa.SkillBuilders.standard()
.addRequestHandlers(
LaunchHandler
)
.addRequestInterceptors(GetUserDataInterceptor)
.withTableName('Spiele')
.withAutoCreateTable(true)
.withDynamoDbClient()
.lambda();
can anyone tell me what i'm doing wrong?
please confirm the partition key is 'userId' not 'UserId' (notice the uppercase U).
Also I would suggest using 'this' object.
Let me know if that helps.
Cheers
Below code is for python lambda function
from ask_sdk_core.skill_builder import CustomSkillBuilder
from ask_sdk_dynamodb.adapter import DynamoDbAdapter
sb = SkillBuilder()
sb = CustomSkillBuilder(persistence_adapter = dynamodb_adapter)

Ionic refresher get's fired automatically on page load?

Ionic refresher seems to be refreshing the page without being manually calling the doRefresh. I would like the refresher to only execute when the "pull down" action is done.
Seems like doRefresh is executed on "ionviewdidload" function automatically.
<ion-refresher (ionRefresh)="doRefresh($event);">
<ion-refresher-content
pullingText="Pull to refresh" pullingIcon="arrow-dropdown"
refreshingSpinner="circles"
refreshingText="..fetching">
</ion-refresher-content>
</ion-refresher>
home.ts
doRefresh(refresher) {
console.log('the current tab that is set = ' + this.tabSelId);
console.log('testing');
this.user = JSON.parse(window.localStorage.getItem('user'));
let self_ = this;
let devicePos = null;
let devicelat = null;
let devicelong = null;
Geolocation.getCurrentPosition().then((position) => {
// self_.loadingData(devicePos);
devicelat = position.coords.latitude;
devicelong = position.coords.longitude;
self_.get_all_posts(devicelat, devicelong, self_.tabSelId);
refresher.complete();
}, (err) => {
console.log('failed to get lat and long :' + err);
self_.get_all_posts(devicelat, devicelong, self_.tabSelId);
// self_.filter_posts_by_type(self_.tabSelId);
// loading.dismiss();
// refresher.complete();
});
}
on my ionviewdidload function (home.ts):
ionViewDidLoad(){
this.user = JSON.parse(window.localStorage.getItem('user'));
let self_ = this;
let devicePos = null;
console.log('the current tab that is set = '+this.tabSelId);
let devicelat = null;
let devicelong = null;
Geolocation.getCurrentPosition().then((position) => {
devicelat = position.coords.latitude;
devicelong = position.coords.longitude;
console.log('%c executing when position is got successfully ', 'background: #222; color: #bada55');
self_.get_all_posts(devicelat, devicelong, self_.tabSelId);
console.log('executing when position is got successfully');
}, (err) => {
console.log('failed to get lat and long :' + err);
devicelat = 28.318237;
devicelong = 111.168137;
self_.get_all_posts(devicelat, devicelong, self_.tabSelId);
});
is it the default behaviour when using
"ion-refresher"
I would like the refresher to only fire when "pull down" action is done.
Don't know why the doRefresh() function is being executed when the app is loaded (the first time only)
In your Html use like..
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingIcon="arrow-dropdown" pullingText="Pull to refresh" refreshingSpinner="circles"
refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
In .ts use like...
doRefresh(event) {
this.userPost();
setTimeout(() => {
event.target.complete();
}, 2000);
}

Populate the cloud firestore document containing an array of document references

I have a collection named campgrounds in which every document contains an array of document reference to the documents in the comments collections.
It looks like this Campground
I'm trying to figure out a way to populate this comments array before sending it to my ejs template.
My code looks like this
app.get("/campgrounds/:docId", function(req, res) {
var docRef = firestore.collection("campgrounds").doc(req.params.docId);
try {
docRef.get().then(doc => {
if (!doc.exists) {
res.send("no such document");
} else {
// res.send(doc.data());
res.render("campground", {
doc: doc.data(),
title: doc.data().title,
id: req.params.docId
});
}
});
} catch (error) {
res.send(error);
}
});
In your array you store DocumentReferences. If you want to get the data of the corresponding documents in order to include this data in your object you should use Promise.all() to execute the variable number (1 or more) of get() asynchronous operations.
The following should work (not tested at all however):
app.get("/campgrounds/:docId", function(req, res) {
var docRef = firestore.collection("campgrounds").doc(req.params.docId);
try {
var campground = {};
docRef.get()
.then(doc => {
if (!doc.exists) {
res.send("no such document");
} else {
campground = {
doc: doc.data(),
title: doc.data().title,
id: req.params.docId
};
var promises = [];
doc.data().comments.forEach((element, index) => {
promises.push(firestore.doc(element).get());
});
return Promise.all(promises);
}
})
.then(results => {
var comments = {};
results.forEach((element, index) => {
comments[index] = element.data().title //Let's imagine a comment has a title property
});
campground.comments = comments;
res.render("campground", campground);
})
} catch (error) {
res.send(error);
}
});
Note that with this code you are doing 1 + N queries (N being the length of the comments array). You could denormalize your data and directly store in the campground doc the data of the comments: you would then need only one query.

AngularFire2 Firebase Observable never ends (list is empty)

I'm trying to query an empty firebase list. The problem is that the observable method subscribe never finish and I can't show to user that ddbb list is empty.
The function getUserAppointmentsByDate(...) is calling getUserAppointments(...), where this.database.list('/appointment/users/' + user_uid) is an empty firebase list for the input user (user_uid).
how should I manage an empty query to firebase?
thanks in advance!
getUserAppointmentsByDate(user_uid: string, start: string, end: string) {
if (typeof (user_uid) == "undefined" || typeof (start) == "undefined" || typeof (end) == "undefined") {
console.error("invalid argument for getPatientReport");
return;
}
return this.getUserAppointments(user_uid)
.map(
(appointment) => {
return appointment
.filter((appointment) => {
var appointmentStart = new Date(appointment.start);
var startFilter = new Date(start);
var endFilter = new Date(end);
//Filter old, not cancelled and not deleted
return (appointmentStart.getTime() < endFilter.getTime())
&& (appointmentStart.getTime() > startFilter.getTime())
&& (appointment.status != AppointmentStatus.CANCELLED);
});
})
}
getUserAppointments(user_uid: string): any {
return this.database.list('/appointment/users/' + user_uid) //*THIS IS AN EMPTY LIST
.mergeMap((appointments) => {
return Observable.forkJoin(appointments.map(
(appointment) => this.database.object('/appointment/list/' + appointment.$key)
.take(1)))
})
}
As the this.database.list('/appointment/users/' + user_uid) return a empty array. Observable.forkJoin(appointments.map( complete without emit any value (that is the expected way of forkJoin works). In this case, you have two options, handling in the complete function.
.subscribe(
res => console.log('I got values'),
err => console.log('I got errors'),
// do it whatever you want here
() => console.log('I complete with any values')
)
or handle in an if statement:
import { of } from 'rxjs/observable/of';
...
return this.database.list('/appointment/users/' + user_uid)
.mergeMap((appointments) => {
if (appointments.length === 0) return of([]);
return Observable.forkJoin(appointments.map(
(appointment) => this.database.object('/appointment/list/' + appointment.$key)
.take(1)))
})

Resources