Извлеките JSON из URL-адреса и преобразуйте его в облачную коллекцию Firestore с облачными функциями

# #node.js #firebase #google-cloud-functions

Вопрос:

Вот чего я хочу добиться : я хочу ежедневно получать JSON с URL-адреса и конвертировать его в коллекцию облачных магазинов firestore, чтобы иметь возможность использовать его в своем приложении Flutter. В идеале сценарий будет добавлять только новые данные в коллекцию.

Я увидел, что могу использовать scheduler облачные функции Firebase для ежедневного выполнения задач. Сейчас это не проблема.

Однако я не знаю, как правильно использовать облачные функции Firebase для получения данных из URL-адреса и преобразования их в коллекцию. Может быть, дело не в облачных функциях, и я что — то неправильно понял. Итак, первый вопрос : Могу ли я запускать классические функции NodeJS внутри облачных функций? Я полагаю, что могу

Затем я инициализировал проект облачной функции локально, подключил его к своей учетной записи Google и начал писать код index.js .

 const functions = require("firebase-functions");
const admin = require('firebase-admin');
const fetch = require('node-fetch');

const db = admin.firestore();
const collectionToiletRef = db.collection('mycollection');

let settings = { method: "Get" };

let url = "my-url.com"

fetch(url, settings)
    .then(res => res.json())
    .then((json) => {
        print(json);
        // TODO for each json object, add new document
    });
 

Второй вопрос : Как я могу запустить этот код, чтобы проверить, работает ли он ? Я видел, что эмулятор можно использовать, но как я могу визуально проверить свою коллекцию cloud firestore ? В этом простом примере я хочу распечатать свой json только для того, чтобы убедиться, что я могу правильно получить данные. Где будет производиться печать ?

Возможно, облачные функции-это не то, что мне нужно для этой задачи. Может быть, мой код плохой. Я не знаю. Спасибо за вашу помощь.

Редактировать

Я пытался это сделать, но звонок никогда не заканчивается. Я думаю, что он ждет обещания, которое никогда не вернется, или что-то в этом роде.

 const functions = require("firebase-functions");
const admin = require('firebase-admin');
const fetch = require('node-fetch');

admin.initializeApp();

const db = admin.firestore();

exports.tempoCF = functions
    .firestore.document('/tempo/{docId}')
    .onCreate(async (snap, context) => {
        console.log("onCreate");

        let settings = { method: "Get" };

        let url = "https://opendata.paris.fr/api/records/1.0/search/?dataset=sanisettesparisamp;q=amp;rows=-1"
        try {
            let response = await fetch(url, settings);
            let json = await response.json();
            // TODO for each json object, add new document
            await Promise.all(json["records"].map(toiletJsonObject => {
                return db.collection('toilets').doc(toiletJsonObject["recordid"]).set({}); // Only to create documents, I will deal with the content later
            }));
        }
        
        catch(error) {
            console.log(error);
            return null;
        }
    }
    );
 

Этот код работает и создает все документы, которые я хочу, но никогда не возвращаю. Однако async (snap, context) => {} переданное onCreate -это Обещание. И это обещание заканчивается, когда Promise.all заканчивается. Я что-то упускаю, но не знаю почему. Я много борюсь с асинхронным программированием с помощью Dart или JS. Не очень ясно у меня в голове.

Ответ №1:

Могу ли я запускать классические функции NodeJS внутри облачных функций?

Конечно! Поскольку метод выборки возвращает обещание, вы можете очень хорошо использовать его в фоновом режиме или в запланированной облачной функции.

Как я могу запустить этот код, чтобы проверить, работает ли он?

Ваш код будет отлично работать в наборе эмуляторов, но вам нужно будет запустить облачную функцию с помощью одной из служб Firebase, которые могут запускаться в эмуляторе. Например, вы можете запустить облачную функцию, создав документ в консоли эмулятора Firestore.

Следующая облачная функция сделает свое дело: просто создайте документ в фиктивной tempo коллекции, и CF добавит новый документ в newDocs коллекцию. Вам решать адаптировать значения полей для этого документа, я только что использовал весь объект JSON.

 exports.tempoCF = functions
    .firestore.document('/tempo/{docId}')
    .onCreate((snap, context) => {

        let settings = { method: "Get" };

        let url = "https://..."

        return fetch(url, settings)
            .then(res => res.json())
            .then((json) => {
                console.log(json);
                // TODO for each json object, add new document

                return admin.firestore().collection('newDocs').add(json);

            })
            .catch(error => {
                console.log(error);
                return null;
            });
    });
 

Вы также можете развернуть свою облачную функцию на серверной части Firebase, и если вы хотите запланировать ее, просто измените код следующим образом (измените триггер).:

 exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {

        let settings = { method: "Get" };

        let url = "https://..."

        return fetch(url, settings)
            .then(res => res.json())
            .then((json) => {
                console.log(json);
                // TODO for each json object, add new document

                return admin.firestore().collection('newDocs').add(json);

            })
            .catch(error => {
                console.log(error);
                return null;
            });
    });
 

Правка после вашей правки:

Следующий код корректно работает в эмуляторе, создавая документы в toilets коллекции.

 exports.tempoCF = functions.firestore
    .document('/tempo/{docId}')
    .onCreate(async (snap, context) => {
        console.log('onCreate');

        let settings = { method: 'Get' };

        let url =
            'https://opendata.paris.fr/api/records/1.0/search/?dataset=sanisettesparisamp;q=amp;rows=-1';
        try {
            let response = await fetch(url, settings);
            let json = await response.json();

            return Promise.all(   // Here we return the promise returned by Promise.all(), so the life cycle of the CF is correctly managed
                json['records'].map((toiletJsonObject) => {
                    admin
                        .firestore()
                        .collection('toilets')
                        .doc(toiletJsonObject['recordid'])
                        .set({ adresse: toiletJsonObject.fields.adresse });
                })
            );
        } catch (error) {
            console.log(error);
            return null;
        }
    });
 

Комментарии:

1. Тх, я попробую это сделать ! Но зачем мне нужно инкапсулировать код в CF ? Нет никакого способа запустить функцию NodeJS за пределами CF ? Кроме того, я думал, что все export дело в том, чтобы создать конечную точку, которую можно вызвать с помощью http-запроса. Будет ли экспортированная функция запускаться автоматически при выполнении файла JS в эмуляторе ? Наконец, есть ли способ создать что-то вроде фабрики из JSON, которая только добавляет новые записи в Firestore, или мне нужно сделать это вручную ?

2. Мне удалось заставить эмулятор работать, спасибо. Однако, когда я запускаю эмулятор, он запускает index.js и, я полагаю, создает конечную точку для tempoCF, но эта функция не вызывается. Как я могу запустить его вручную перед тестированием запланированной функции ? Я должен позвонить что-то вроде localhost:5001/tempoCF ?

3. «нужно ли мне инкапсулировать код в CF» => Да, см. > firebase.google.com/docs/reference/functions/cloud_functions_ . Есть ли способ создать что-то вроде фабрики из JSON => Вы можете использовать любой код JavaScript/машинописи или библиотеку для создания объектов, которые вы используете для создания документов Firestore.

4. «Как я могу запустить его вручную перед тестированием запланированной функции ?» => Я привел пример с триггером Firestore в своем ответе (> tempo коллекция). Вы можете запустить его в эмуляторе как функцию HTTP , но поскольку вы собираетесь развернуть его как запланированную функцию, я бы рекомендовал использовать фоновый триггер, так как код будет таким же, как и для запланированной функции.

5. Спасибо за ответы! Я отредактировал свой код, так как есть кое-что, чего я не понимаю в асинхронных функциях, если вы не возражаете, взгляните на это.