Интегрируйте puppeteer в прослушиватель облачных функций Google

Я запускаю облачные функции Google в среде выполнения узла 8 с 2 г, выделенным для прослушивателя. Слушатель: работает без кукловода; Puppeteer: работает при использовании внутри запроса на получение;

Но я хочу, чтобы кукольник работал внутри моего слушателя, из-за чего мой уже действительный слушатель выдавал ошибку.

Я прочитал много документов, и ни один из них не интегрирует кукловод внутри слушателя.

Обновлено: я также пытался изменить тип возвращаемого значения на допустимый формат, но безрезультатно.

Мой действительный слушатель

exports.listen = functions.firestore
    .document('/request/{id}')
    .onWrite((change, context) => {
        // Grab the data from the original function trigger 
        const document = await change.after.data()['data'];

        // Dummy return of what will eventually be given
        return change.after.ref.set(
            {
                results: [
                    { title: 'New Title', Country: 'Lala Land' },
                    { title: 'New Job', Country: 'Asgardia' }
                ]
            },
            { merge: true }
        );
    });

Действительная функция кукловода

const express = require('express');
const functions = require('firebase-functions');
const puppeteer = require('puppeteer');
const app = express();
// Runs before every route. Launches headless Chrome.
app.all('*', async (req, res, next) => {
  // Note: --no-sandbox is required in this env.
  // Could also launch chrome and reuse the instance
  // using puppeteer.connect()
  res.locals.browser = await puppeteer.launch({
    args: ['--no-sandbox']
  });
  next(); // pass control to next route.
});
// Handler to take screenshots of a URL.
app.get('/screenshot', async function screenshotHandler(req, res) {
  const url = req.query.url;
  if (!url) {
    return res.status(400).send(
      'Please provide a URL. Example: ?url=https://example.com');
  }
  const browser = res.locals.browser;
  try {
    const page = await browser.newPage();
    await page.goto(url, {waitUntil: 'networkidle2'});
    const buffer = await page.screenshot({fullPage: true});
    res.type('image/png').send(buffer);
  } catch (e) {
    res.status(500).send(e.toString());
  }
  await browser.close();
});

Неверный слушатель с кукловодом изменен на минимальные потребности

exports.listen = functions.firestore
    .document('/request/{id}')
    .onWrite((change, context) => {
        const document = change.after.data()['data'];
        async function benchmark() {

            const browser = await puppeteer.launch({
                args: ['--no-sandbox']
            });
            const page = await browser.newPage();
            await page.goto('http://picocms.org/', {
                waitUntil: 'networkidle2'
            });
            const content = await page.content();
            return content;
        }

        return change.after.ref.set(
            {
                // results: [
                //  { title: 'New Title', Country: 'Lala Land' },
                //  { title: 'New Job', Country: 'Asgardia' }
                // ]
                results: benchmark()
            },
            { merge: true }
        );
    });

Ошибка в журналах

Error: Value for argument "data" is not a valid Firestore document. Input is not a plain JavaScript object (found in field results).
    at Object.validateUserInput (/srv/node_modules/@google-cloud/firestore/build/src/serializer.js:312:15)
    at validateDocumentData (/srv/node_modules/@google-cloud/firestore/build/src/write-batch.js:622:26)
    at WriteBatch.set (/srv/node_modules/@google-cloud/firestore/build/src/write-batch.js:242:9)
    at DocumentReference.set (/srv/node_modules/@google-cloud/firestore/build/src/reference.js:337:27)
    at exports.listen.functions.firestore.document.onWrite (/srv/index.js:57:27)
    at cloudFunctionNewSignature (/srv/node_modules/firebase-functions/lib/cloud-functions.js:114:23)
    at /worker/worker.js:825:24
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)

Если у кого-то есть собственное творение минимального прослушивателя с интегрированным кукловодом, не стесняйтесь сбрасывать его рабочий фрагмент.

Christopher Bradley 10.04.2019 14:46

Ваше сообщение об ошибке, похоже, не имеет ничего общего с кукловодом. Я бы сказал, что у вас проблема с этой строкой: const document = change.after.data()['data'];

Doug Stevenson 10.04.2019 14:57

Дело в том, что без кода кукловода он работает правильно, я проверил его без ошибок, и он выдает правильный результат.

Christopher Bradley 10.04.2019 15:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
945
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Они действительно должны сделать документы для этого использования, но я нашел свое собственное решение, мне просто нужно согласиться с использованием кукловода в сообщении или получить запрос, поскольку у вас не будет доступа к параметрам (запрос, ответ).

Для тех, кто хочет использовать это по-своему, вот минимальное жизнеспособное решение, на основе которого можно построить.

Примечание. Я обновил среду выполнения до Node 10 (бета) с выделенным 2G.

const functions = require('firebase-functions');
const puppeteer = require('puppeteer');
const app = require('express')();
const { db } = require('./util/admin');

// Runs before every route. Launches headless Chrome.
app.all('*', async (req, res, next) => {
    // Note: --no-sandbox is required in this env.
    // Could also launch chrome and reuse the instance
    // using puppeteer.connect()
    res.locals.browser = await puppeteer.launch({
        args: ['--no-sandbox']
    });
    next(); // pass control to next route.
});

app.post('/create', async (req, res) => {
    const request = {
        data: req.body.data,
        created: new Date().toISOString()
    };
    const browser = res.locals.browser;
    const page = await browser.newPage();
    await page.goto('http://picocms.org/', {
        waitUntil: 'networkidle2'
    });
    const content = await page.content();
    request.data = JSON.stringify(content);
    await browser.close();
    db.collection('request')
        .add(request)
        .then(doc => {
            return res.json({
                message: `[success] ${doc.id} Generated`
            });
        })
        .catch(e => {
            res.status(500).json({
                error: `[failed] No Request Was Saved _> ${e}`
            });
        });
});

exports.api = functions.https.onRequest(app);

Ваша функция benchmark() — это async, поэтому при вызове она возвращает обещание.

Поэтому вы пытаетесь сохранить обещание здесь:

        return change.after.ref.set(
            {
                results: benchmark()     // This is a promise
            },
            { merge: true }
        );

Вместо этого вы хотите сохранить разрешенное значение из обещания. Вы должны изменить свою функцию на async, а затем добавить await перед вызовом benchmark(). Тогда ваши данные станут «простым объектом JavaScript», и их можно будет сериализовать и хранить.

Другие вопросы по теме