0
votes

For a scheduling app I'm building in Flutter I'm trying to write data to my cloud Firestore database with cloud functions and cron jobs to make my app more self-sustaining. I'm stuck at the first step, which is trying to get my cloud function to write data to my cloud Firestore database.

Below I've included links to pictures on github of how my current data is structured in Firestore. What I want is to add named documents to the collection 'days' with a subcollection of 'hours', in which there are more named documents with the fields 'hour' and 'reserved'.

Picture 1: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11-07%20om%2014.27.55.png?raw=true

Picture 2: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11-07%20om%2014.28.26.png?raw=true

Below I have also included my try on getting data in Firestore.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

// admin.firestore().collection('days').add({original: original}). then(writeResult => {
//
// });

exports.updateData = functions.https.onCall((data, context) => {
    const days = admin.firestore().collection('days');
    return days.doc(data['2019-11-08/hours/001']).set({
        hour: '08:00-09:00',
        reserved: '',
    });
});

Ideally I would want the cloud function to add 14 documents (eg. 2019-11-06 to 2019-11-19) to the collection 'days' at the same time. Those documents would then each have a subcollection 'hours' with the data from the second screenshot (so the documents 001, 002, 003 etc. with the fields hour and reserved).

I read the documentation on cloud functions and mostly found triggers when data is written to Firestore, but this is not what I want. I also tried the quickstart function samples but with no success. It shouldn't be too hard but I can't seem to figure it out. I'm using Javascript to write the cloud function in. Any help/tips would be greatly appreciated!

2

2 Answers

0
votes

if you wanna do a cron in firebase maybe its better if you use Google Cloud Scheduler, but be careful this approach have a different kind of facturation

    exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
  console.log('This will be run every 5 minutes!');
  return null;
});

You can learn more about this in:

https://firebase.google.com/docs/functions/schedule-functions

0
votes

You didn't specify exactly what wasn't working for you. However, the first thing I notice is that you're not calling initilizeApp properly from the cloud functions context. For cloud functions, you don't need any parameters, as the default credentials should work for you (unless you have done something very unusual already).

Here is a cloud function that will model the behavior you want. It does not do the full behavior, as I found that writing the date handling code would likely distract from the main part of the problem you are asking about, which is the firestore and functions code itself.

Likewise, this uses an https function (as it is a bit easier for me to test :), to use a callable function (or any other function type, e.g. a scheduled function if you're using a cron job) you would need to adjust it slightly (e.g., for a callable function you would need to change the declaration back to onCall and changing the final .then() call to return the value you want to return).

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

exports.doIt = functions.https.onRequest((request, response) => {
    const days = db.collection('days');
    const writePromises = [];

    ['2019-11-08', '2019-11-09'].forEach((day) => {
      const documentReference = days.doc(day);
      writePromises.push(documentReference.set({reserved: ''}));

      const hoursReference = documentReference.collection('hours');
      const dataMap = { '001': '08:00-09:00',
                        '002': '09:00-10:00',
                        '003': '10:00-11:00',
                        '004': '11:00-12:00' };
      Object.keys(dataMap).forEach((hour) => {
        writePromises.push(hoursReference.doc(hour).set({
          hour: dataMap[hour],
          reserved: ''
        }))
      });
    });

    return Promise.all(writePromises)
      .then(() => { response.send('ok'); })
      .catch((err) => { console.log(err); });
  });

Note that when you write this entire structure, you will be billed for a write to each document. There isn't really a way to avoid that though, as writes are billed per document, not per request.

Likewise, you may want to consider doing this as a batched write -- the above example is just showing a basic approach to writing. A batched write would put all of the documents into the database atomically. However, a batched write is limited to 500 updates, which you would hit at around 21 days of data (21 days * 24 hours). This would look very similar to the above (below is just the content of the function itself:

    const days = db.collection('days');
    const batch = db.batch();

    ['2019-11-08', '2019-11-09'].forEach((day) => {
      const documentReference = days.doc(day);
      batch.set(documentReference, {reserved: ''});

      const hoursReference = documentReference.collection('hours');
      const dataMap = { '001': '08:00-09:00',
                        '002': '09:00-10:00',
                        '003': '10:00-11:00',
                        '004': '11:00-12:00' };
      Object.keys(dataMap).forEach((hour) => {
        batch.set(hoursReference.doc(hour), {
          hour: dataMap[hour],
          reserved: ''
        });
      });
    });

    return batch.commit()
      .then(() => { response.send('ok'); })
      .catch((err) => { console.log(err); });

I do wonder a small bit about why you need to do this in a cloud function, rather than via a Firestore call directly from your app. Regardless, the above should allow you to get started.