Начните создавать расширение

На этой странице описаны шаги, необходимые для создания простого расширения Firebase, которое вы можете установить в свои проекты или поделиться им с другими. Этот простой пример расширения Firebase будет отслеживать сообщения в вашей базе данных Realtime Database и преобразовывать их в верхний регистр.

1. Настройте свою среду и инициализируйте проект.

Прежде чем приступить к разработке расширения, вам потребуется настроить среду сборки с необходимыми инструментами.

  1. Установите Node.js версии 16 или новее. Один из способов установки Node — использование nvm (или nvm-windows ).

  2. Установите или обновите Firebase CLI до последней версии. Для установки или обновления с помощью npm выполните следующую команду:

    npm install -g firebase-tools

Теперь воспользуйтесь интерфейсом командной строки Firebase для инициализации нового проекта расширения:

  1. Создайте директорию для вашего расширения и перейдите в неё с помощью cd :

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
  2. Выполните команду ext:dev:init в Firebase CLI:

    firebase ext:dev:init

    При появлении запроса выберите JavaScript в качестве языка для функций (но обратите внимание, что при разработке собственного расширения вы также можете использовать TypeScript), а при запросе на установку зависимостей ответьте «да». (Для остальных параметров примите значения по умолчанию.) Эта команда создаст базовый код для нового расширения, с которого вы сможете начать разработку своего расширения.

2. Попробуйте запустить пример расширения с помощью эмулятора.

Когда Firebase CLI инициализировал новый каталог расширений, он создал простую примерную функцию и каталог integration-tests , содержащий файлы, необходимые для запуска расширения с помощью набора инструментов эмулятора Firebase.

Попробуйте запустить пример расширения в эмуляторе:

  1. Перейдите в каталог integration-tests :

    cd functions/integration-tests
  2. Запустите эмулятор с демонстрационным проектом:

    firebase emulators:start --project=demo-test

    Эмулятор загружает расширение в заранее определенный «фиктивный» проект ( demo-test ). На данный момент расширение состоит из одной функции, запускаемой по HTTP-запросу, greetTheWorld , которая при обращении возвращает сообщение «hello world».

  3. При работающем эмуляторе попробуйте использовать функцию greetTheWorld расширения, перейдя по URL-адресу, который отобразился при его запуске.

    В вашем браузере отображается сообщение "Hello World from greet-the-world".

  4. Исходный код этой функции находится в каталоге functions расширения. Откройте исходный код в любом редакторе или IDE по вашему выбору:

    functions/index.js

    const functions = require("firebase-functions/v1");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. Во время работы эмулятора он будет автоматически перезагружать все изменения, внесенные вами в код функций. Попробуйте внести небольшое изменение в функцию greetTheWorld :

    functions/index.js

    const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
    

    Сохраните изменения. Эмулятор перезагрузит ваш код, и теперь при переходе по URL-адресу функции вы увидите обновленное приветствие.

3. Добавьте основную информацию в файл extension.yaml.

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

В качестве первого, скромного шага, отредактируйте предопределенные метаданные расширения, чтобы они отражали расширение, которое вы хотите использовать вместо greet-the-world . Эти метаданные хранятся в файле extension.yaml .

  1. Откройте файл extension.yaml в редакторе и замените всё его содержимое следующим:

    name: rtdb-uppercase-messages
    version: 0.0.1
    specVersion: v1beta  # Firebase Extensions specification version; don't change
    
    # Friendly display name for your extension (~3-5 words)
    displayName: Convert messages to upper case
    
    # Brief description of the task your extension performs (~1 sentence)
    description: >-
      Converts messages in RTDB to upper case
    
    author:
      authorName: Your Name
      url: https://your-site.example.com
    
    license: Apache-2.0  # Required license
    
    # Public URL for the source code of your extension
    sourceUrl: https://github.com/your-name/your-repo
    

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

  2. Поскольку вы изменили название своего расширения, вам также следует обновить конфигурацию эмулятора, указав новое название:

    1. В functions/integration-tests/firebase.json замените greet-the-world на rtdb-uppercase-messages .
    2. Переименуйте functions/integration-tests/extensions/greet-the-world.env в functions/integration-tests/extensions/rtdb-uppercase-messages.env .

В коде вашего расширения еще остались некоторые элементы расширения greet-the-world , но пока оставьте их. Вы обновите их в следующих разделах.

4. Напишите облачную функцию и объявите её как ресурс расширения.

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

  1. Откройте исходный код функций расширения (в каталоге functions расширения) в любом редакторе или IDE. Замените его содержимое следующим:

    functions/index.js

    import { database, logger } from "firebase-functions/v1";
    
    const app = initializeApp();
    
    // Listens for new messages added to /messages/{pushId}/original and creates an
    // uppercase version of the message to /messages/{pushId}/uppercase
    // for all databases in 'us-central1'
    export const makeuppercase = database
      .ref("/messages/{pushId}/uppercase")
      .onCreate(async (snapshot, context) => {
        // Grab the current value of what was written to the Realtime Database.
        const original = snapshot.val();
    
        // Convert it to upper case.
        logger.log("Uppercasing", context.params.pushId, original);
        const uppercase = original.toUpperCase();
    
        // Setting an "uppercase" sibling in the Realtime Database.
        const upperRef = snapshot.ref.parent.child("upper");
        await upperRef.set(uppercase);
    });
    

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

    Кстати, в этом новом файле используется синтаксис модулей ECMAScript ( import и export ) вместо CommonJS ( require ). Чтобы использовать модули ES в Node, укажите "type": "module" в functions/package.json :

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      
    }
    
  2. Каждая функция вашего расширения должна быть объявлена ​​в файле extension.yaml . В примере расширения в качестве единственной облачной функции была объявлена ​​функция greetTheWorld ; теперь, когда вы заменили её на makeuppercase , вам также необходимо обновить её объявление.

    Откройте extension.yaml и добавьте поле resources :

    resources:
      - name: makeuppercase
        type: firebaseextensions.v1beta.function
        properties:
          eventTrigger:
            eventType: providers/google.firebase.database/eventTypes/ref.create
            # DATABASE_INSTANCE (project's default instance) is an auto-populated
            # parameter value. You can also specify an instance.
            resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
          runtime: "nodejs18"
    
  3. Поскольку ваше расширение теперь использует Realtime Database в качестве триггера, вам необходимо обновить конфигурацию эмулятора, чтобы запускать эмулятор RTDB одновременно с эмулятором Cloud Functions:

    1. Если эмулятор все еще запущен, остановите его, нажав Ctrl-C.

    2. В каталоге functions/integration-tests выполните следующую команду:

      firebase init emulators

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

    3. Перезапустите эмулятор:

      firebase emulators:start --project=demo-test
  4. Попробуйте обновленное расширение:

    1. Откройте пользовательский интерфейс эмулятора базы данных, используя ссылку, которая отобразилась при его запуске.

    2. Отредактируйте корневой узел базы данных:

      • Поле: messages
      • Тип: json
      • Значение: {"11": {"original": "recipe"}}

      Если все настроено правильно, при сохранении изменений в базе данных должна сработать функция makeuppercase расширения, добавив дочернюю запись в сообщение 11 со следующим содержимым "upper": "RECIPE" . Проверьте журналы и вкладки базы данных в пользовательском интерфейсе эмулятора, чтобы убедиться в ожидаемых результатах.

    3. Попробуйте добавить еще несколько дочерних элементов к узлу messages ( {"original":"any text"} ). При добавлении новой записи расширение должно добавлять поле uppercase , содержащее содержимое original поля в верхнем регистре.

Теперь у вас есть полноценное, хотя и простое, расширение, работающее с экземпляром RTDB. В следующих разделах вы доработаете это расширение, добавив несколько новых функций. Затем вы подготовите расширение к распространению среди других пользователей и, наконец, узнаете, как опубликовать свое расширение на Extensions Hub.

5. Объявите API и роли.

Firebase предоставляет каждому экземпляру установленного расширения ограниченный доступ к проекту и его данным с помощью учетной записи службы для каждого экземпляра. Каждая учетная запись имеет минимальный набор разрешений, необходимых для работы. По этой причине необходимо явно указать все роли IAM, необходимые вашему расширению; когда пользователи устанавливают ваше расширение, Firebase создает учетную запись службы с предоставленными этими ролями и использует ее для запуска расширения.

Для запуска событий продукта не требуется объявлять роли, но для взаимодействия с продуктом роль необходимо объявить. Поскольку добавленная вами на предыдущем шаге функция записывает данные в базу данных реального времени, необходимо добавить следующее объявление в файл extension.yaml :

roles:
  - role: firebasedatabase.admin
    reason: Allows the extension to write to RTDB.

Аналогичным образом, вы указываете API Google, которые использует расширение, в поле apis . Когда пользователи установят ваше расширение, им будет предложено автоматически включить эти API для своего проекта. Обычно это необходимо только для API Google, отличных от Firebase, и в данном руководстве это не требуется.

6. Определите параметры, настраиваемые пользователем.

Функция, созданная вами на двух предыдущих шагах, отслеживала входящие сообщения в определенном месте базы данных RTDB. Иногда отслеживание определенного места действительно необходимо, например, когда ваше расширение работает со структурой базы данных, которую вы используете исключительно для своего расширения. Однако в большинстве случаев вам потребуется сделать эти значения настраиваемыми для пользователей, которые устанавливают ваше расширение в свои проекты. Таким образом, пользователи смогут использовать ваше расширение для работы со своей существующей базой данных.

Сделайте путь, по которому расширение будет отслеживать новые сообщения, настраиваемым пользователем:

  1. В файл extension.yaml добавьте раздел params :

    - param: MESSAGE_PATH
      label: Message path
      description: >-
        What is the path at which the original text of a message can be found?
      type: string
      default: /messages/{pushId}/original
      required: true
      immutable: false
    

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

  2. Оставаясь в файле extension.yaml , вернитесь к объявлению makeuppercase и измените поле resource следующим образом:

    resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
    

    Токен ${param:MESSAGE_PATH} — это ссылка на только что определенный вами параметр. При запуске расширения этот токен будет заменен значением, заданным пользователем для этого параметра, в результате чего функция makeuppercase будет отслеживать указанный пользователем путь. Вы можете использовать этот синтаксис для ссылки на любой заданный пользователем параметр в любом месте файла extension.yaml (и в POSTINSTALL.md — подробнее об этом позже).

  3. Вы также можете получить доступ к определяемым пользователем параметрам из кода ваших функций.

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

    functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
    

    Обратите внимание, что в Firebase Extensions это изменение внесено исключительно в целях документирования: при развертывании облачной функции в составе расширения используется определение триггера из файла extension.yaml , и игнорируется значение, указанное в определении функции. Тем не менее, рекомендуется документировать в коде, откуда берется это значение.

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

    functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate(
      async (snapshot, context) => {
        logger.log("Found new message at ", snapshot.ref);
    
        // Grab the current value of what was written to the Realtime Database.
        ...
    
  5. Обычно пользователям предлагается указать значения параметров при установке расширения. Однако при использовании эмулятора для тестирования и разработки процесс установки пропускается, поэтому вместо этого значения для определяемых пользователем параметров указываются с помощью файла env .

    Откройте functions/integration-tests/extensions/rtdb-uppercase-messages.env и замените определение GREETING следующим:

    MESSAGE_PATH=/msgs/{pushId}/original
    

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

  6. Теперь перезапустите эмулятор и снова перейдите в пользовательский интерфейс эмулятора базы данных.

    Отредактируйте корневой узел базы данных, используя указанный выше путь:

    • Поле: msgs
    • Тип: json
    • Значение: {"11": {"original": "recipe"}}

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

7. Предоставьте обработчики событий для определяемой пользователем логики.

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

Расширение может предоставлять синхронные , асинхронные или и те, и другие. Синхронные обработчики позволяют пользователям выполнять задачи, которые блокируют завершение одной из функций расширения. Это может быть полезно, например, для предоставления пользователям возможности выполнять пользовательскую предварительную обработку перед тем, как расширение начнет свою работу.

В этом руководстве вы добавите асинхронный хук в свое расширение, который позволит пользователям определять собственные этапы обработки, которые будут выполняться после того, как ваше расширение запишет сообщение в верхнем регистре в базу данных Realtime Database. Асинхронные хуки используют Eventarc для запуска определяемых пользователем функций. Расширения объявляют типы событий, которые они генерируют, и при установке расширения пользователи выбирают, какие типы событий их интересуют. Если они выбирают хотя бы одно событие, Firebase создаст канал Eventarc для расширения в рамках процесса установки. Затем пользователи могут развернуть свои собственные облачные функции, которые будут прослушивать этот канал и запускаться, когда расширение публикует новые события.

Выполните следующие шаги, чтобы добавить асинхронный хук:

  1. В файл extension.yaml добавьте следующий раздел, который объявляет единственный тип события, генерируемый расширением:

    events:
      - type: test-publisher.rtdb-uppercase-messages.v1.complete
        description: >-
          Occurs when message uppercasing completes. The event subject will contain
          the RTDB URL of the uppercase message.
    

    Типы событий должны быть уникальными для всех; для обеспечения уникальности всегда называйте свои события в следующем формате: <publisher-id>.<extension-id>.<version>.<description> . (У вас пока нет идентификатора издателя, поэтому используйте пока test-publisher .)

  2. В конце функции makeuppercase добавьте код, который отправляет событие того типа, который вы только что объявили:

    functions/index.js

    // Import the Eventarc library:
    import { initializeApp } from "firebase-admin/app";
    import { getEventarc } from "firebase-admin/eventarc";
    
    const app = initializeApp();
    
    // In makeuppercase, after upperRef.set(uppercase), add:
    
    // Set eventChannel to a newly-initialized channel, or `undefined` if events
    // aren't enabled.
    const eventChannel =
      process.env.EVENTARC_CHANNEL &&
      getEventarc().channel(process.env.EVENTARC_CHANNEL, {
        allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
      });
    
    // If events are enabled, publish a `complete` event to the configured
    // channel.
    eventChannel &&
      eventChannel.publish({
        type: "test-publisher.rtdb-uppercase-messages.v1.complete",
        subject: upperRef.toString(),
        data: {
          "original": original,
          "uppercase": uppercase,
        },
      });
    

    В этом примере кода используется тот факт, что переменная среды EVENTARC_CHANNEL определяется только в том случае, если пользователь включил хотя бы один тип событий. Если EVENTARC_CHANNEL не определена, код не пытается публиковать какие-либо события.

    К событию Eventarc можно добавить дополнительную информацию. В приведенном выше примере событие имеет поле subject , содержащее ссылку на вновь созданное значение, и полезную нагрузку data , содержащую исходное сообщение в верхнем регистре. Пользовательские функции, запускаемые при возникновении этого события, могут использовать эту информацию.

  3. Обычно переменные среды EVENTARC_CHANNEL и EXT_SELECTED_EVENTS определяются на основе параметров, выбранных пользователем во время установки. Для тестирования с эмулятором необходимо вручную определить эти переменные в файле rtdb-uppercase-messages.env :

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

На этом этапе вы выполнили все необходимые шаги для добавления асинхронного обработчика событий в ваше расширение.

Чтобы опробовать эту новую функцию, которую вы только что внедрили, на следующих нескольких шагах возьмите на себя роль пользователя, устанавливающего расширение:

  1. В каталоге functions/integration-tests инициализируйте новый проект Firebase:

    firebase init functions

    При появлении запроса откажитесь от создания проекта по умолчанию, выберите JavaScript в качестве языка Cloud Functions и установите необходимые зависимости. Этот проект представляет собой проект пользователя , в котором установлено ваше расширение.

  2. Отредактируйте integration-tests/functions/index.js и вставьте следующий код:

    import { logger } from "firebase-functions/v1";
    import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
    
    import { initializeApp } from "firebase-admin/app";
    import { getDatabase } from "firebase-admin/database";
    
    const app = initializeApp();
    
    export const extraemphasis = onCustomEventPublished(
      "test-publisher.rtdb-uppercase-messages.v1.complete",
      async (event) => {
        logger.info("Received makeuppercase completed event", event);
    
        const refUrl = event.subject;
        const ref = getDatabase().refFromURL(refUrl);
        const upper = (await ref.get()).val();
        return ref.set(`${upper}!!!`);
      }
    );
    

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

  3. Перезапустите эмулятор. Эмулятор загрузит функции расширения, а также функцию постобработки, определенную пользователем.

  4. Перейдите в пользовательский интерфейс эмулятора базы данных и отредактируйте корневой узел базы данных, используя указанный выше путь:

    • Поле: msgs
    • Тип: json
    • Значение: {"11": {"original": "recipe"}}

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

8. Добавьте обработчики событий жизненного цикла.

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

Расширения Firebase используют Cloud Tasks для запуска обработчиков событий жизненного цикла. Вы определяете обработчики событий с помощью Cloud Functions; всякий раз, когда экземпляр вашего расширения достигает одного из поддерживаемых событий жизненного цикла, если вы определили обработчик, он добавляется в очередь Cloud Tasks. Затем Cloud Tasks асинхронно выполняет обработчик. Во время выполнения обработчика события жизненного цикла консоль Firebase сообщает пользователю, что экземпляр расширения имеет задачу обработки, находящуюся в процессе. Ваша функция-обработчик отвечает за передачу пользователю информации о текущем состоянии и завершении задачи.

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

  1. Определите новую облачную функцию, которая запускается событиями из очереди задач:

    functions/index.js

    import { tasks } from "firebase-functions/v1";
    
    import { getDatabase } from "firebase-admin/database";
    import { getExtensions } from "firebase-admin/extensions";
    import { getFunctions } from "firebase-admin/functions";
    
    export const backfilldata = tasks.taskQueue().onDispatch(async () => {
      const batch = await getDatabase()
        .ref(process.env.MESSAGE_PATH)
        .parent.parent.orderByChild("upper")
        .limitToFirst(20)
        .get();
    
      const promises = [];
      for (const key in batch.val()) {
        const msg = batch.child(key);
        if (msg.hasChild("original") && !msg.hasChild("upper")) {
          const upper = msg.child("original").val().toUpperCase();
          promises.push(msg.child("upper").ref.set(upper));
        }
      }
      await Promise.all(promises);
    
      if (promises.length > 0) {
        const queue = getFunctions().taskQueue(
          "backfilldata",
          process.env.EXT_INSTANCE_ID
        );
        return queue.enqueue({});
      } else {
        return getExtensions()
          .runtime()
          .setProcessingState("PROCESSING_COMPLETE", "Backfill complete.");
      }
    });
    

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

  2. В файле extension.yaml объявите свою функцию заполнения как ресурс расширения, имеющий свойство taskQueueTrigger :

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    Затем объявите эту функцию обработчиком события жизненного цикла onInstall :

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Хотя заполнение существующих сообщений — полезная функция, расширение может функционировать и без неё. В подобных ситуациях следует сделать запуск обработчиков событий жизненного цикла необязательным.

    Для этого добавьте новый параметр в extension.yaml :

    - param: DO_BACKFILL
      label: Backfill existing messages
      description: >-
        Generate uppercase versions of existing messages?
      type: select
      required: true
      options:
        - label: Yes
          value: true
        - label: No
          value: false
    

    Затем в начале функции заполнения проверьте значение параметра DO_BACKFILL и завершите работу функции досрочно, если он не задан:

    functions/index.js

    if (!process.env.DO_BACKFILL) {
      return getExtensions()
        .runtime()
        .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped.");
    }
    

Благодаря вышеуказанным изменениям, расширение теперь будет преобразовывать существующие сообщения в верхний регистр после установки.

До этого момента вы использовали эмулятор расширений для разработки своего расширения и тестирования текущих изменений. Однако эмулятор расширений пропускает процесс установки, поэтому для тестирования обработчика события onInstall вам потребуется установить расширение в реальном проекте. Впрочем, это и к лучшему, поскольку с добавлением этой функции автоматической подстановки код расширения в учебном пособии теперь полностью готов!

9. Разверните в реальном проекте Firebase.

Хотя эмулятор расширений — отличный инструмент для быстрой доработки расширений в процессе разработки, в какой-то момент вам захочется попробовать его в реальном проекте.

Для этого сначала создайте новый проект с включенными сервисами:

  1. В консоли Firebase добавьте новый проект.
  2. Переведите свой проект на тарифный план Blaze с оплатой по мере использования. Для работы Cloud Functions for Firebase требуется, чтобы у вашего проекта был платежный аккаунт, поэтому он также необходим для установки расширения.
  3. В новом проекте включите функцию "База данных в реальном времени" .
  4. Поскольку вы хотите проверить способность вашего расширения заполнять существующие данные при установке, импортируйте несколько примеров данных в ваш экземпляр базы данных реального времени:
    1. Загрузите некоторые исходные данные из базы данных RTDB .
    2. На странице «База данных реального времени» в консоли Firebase нажмите (подробнее) > «Импорт JSON» и выберите только что загруженный файл.
  5. Чтобы функция заполнения пропусков могла использовать метод orderByChild , настройте базу данных для индексации сообщений по значению upper :

    {
      "rules": {
        ".read": false,
        ".write": false,
        "messages": {
          ".indexOn": "upper"
        }
      }
    }
    

Теперь установите расширение из локального источника в новый проект:

  1. Создайте новую директорию для вашего проекта Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Инициализируйте проект Firebase в рабочей директории:

    firebase init database

    При появлении запроса выберите только что созданный вами проект.

  3. Установите расширение в свой локальный проект Firebase:

    firebase ext:install /path/to/rtdb-uppercase-messages

    Здесь вы можете увидеть, как выглядит пользовательский интерфейс при установке расширения с помощью инструмента командной строки Firebase. Обязательно выберите «да», когда инструмент настройки спросит, хотите ли вы заполнить существующую базу данных.

    После выбора параметров конфигурации Firebase CLI сохранит вашу конфигурацию в каталоге extensions и укажет местоположение исходного кода расширения в файле firebase.json . Вместе эти две записи называются манифестом расширений . Пользователи могут использовать манифест для сохранения конфигурации своих расширений и развертывания их в различных проектах.

  4. Разверните конфигурацию расширения в своем рабочем проекте:

    firebase deploy --only extensions

Если все пройдет успешно, Firebase CLI должен загрузить ваше расширение в ваш проект и установить его. После завершения установки запустится задача заполнения базы данных, и через несколько минут она будет обновлена ​​сообщениями, написанными в верхнем регистре. Добавьте несколько новых узлов в базу данных сообщений и убедитесь, что расширение также работает для новых сообщений.

10. Напишите документацию.

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

При инициализации проекта расширения Firebase CLI создал заглушки минимально необходимой документации. Обновите эти файлы, чтобы они точно отражали созданное вами расширение.

extension.yaml

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

Однако не следует недооценивать важность документации, содержащейся в этом файле. Помимо важной идентификационной информации расширения — имени, описания, автора, местоположения официального репозитория — файл extension.yaml содержит документацию для каждого ресурса и настраиваемого пользователем параметра, доступную пользователям. Эта информация отображается пользователям в консоли Firebase, Центре расширений и Firebase CLI.

PREINSTALL.md

В этом файле предоставьте пользователю необходимую информацию перед установкой расширения: кратко опишите его функции, объясните необходимые предварительные условия и предоставьте информацию о платежных последствиях установки расширения. Если у вас есть веб-сайт с дополнительной информацией, это также хорошее место для размещения ссылки на него.

Текст этого файла отображается пользователю в Центре расширений и командой firebase ext:info .

Вот пример файла предварительной установки:

Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.

This extension expects a database layout like the following example:

    "messages": {
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
    }

When you create new string records, this extension creates a new sibling record
with upper-cased text:

    MESSAGE_ID: {
      "original": MESSAGE_TEXT,
      "upper": UPPERCASE_MESSAGE_TEXT,
    }

#### Additional setup

Before installing this extension, make sure that you've
[set up Realtime Database](https://firebase.google.com/docs/database/quickstart)
in your Firebase project.

#### Billing

To install an extension, your project must be on the
[Blaze (pay as you go) plan](https://firebase.google.com/pricing).

- This extension uses other Firebase and Google Cloud Platform services, which
  have associated charges if you exceed the service's no-cost tier:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

Этот файл содержит полезную информацию для пользователей после успешной установки вашего расширения: например, дальнейшие шаги по настройке, пример работы расширения и так далее.

Содержимое файла POSTINSTALL.md отображается в консоли Firebase после настройки и установки расширения. Вы можете ссылаться на параметры пользователя в этом файле, и они будут заменены заданными значениями.

Вот пример файла, который устанавливается после установки расширения с обучающим материалом:

### See it in action

You can test out this extension right away!

1.  Go to your
    [Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.

1.  Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.

1.  In a few seconds, you'll see a sibling node named `upper` that contains the
    message in upper case.

### Using the extension

We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).

### Monitoring

As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.

CHANGELOG.md

Также следует документировать изменения, внесенные между выпусками расширения, в файле CHANGELOG.md .

Поскольку пример расширения ранее никогда не публиковался, в журнале изменений содержится только одна запись:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

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

В рамках данного руководства мы не будем создавать файл README.

Дополнительная документация

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

В рамках данного руководства мы воздержимся от написания более подробной документации.

11. Опубликовать на Extensions Hub

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

Когда вы будете готовы опубликовать свою работу на Extensions Hub, вот как это сделать:

  1. Если вы публикуете свое первое расширение, зарегистрируйтесь в качестве издателя расширений . При регистрации в качестве издателя расширений вы создаете идентификатор издателя, который позволяет пользователям быстро идентифицировать вас как автора ваших расширений.
  2. Разместите исходный код вашего расширения в общедоступном, легко проверяемом месте. Когда ваш код доступен из проверяемого источника, Firebase может опубликовать ваше расширение непосредственно из этого места. Это помогает гарантировать, что вы публикуете актуальную версию вашего расширения, и помогает пользователям, позволяя им изучать код, который они устанавливают в свои проекты.

    В настоящее время это означает размещение вашего расширения в общедоступном репозитории GitHub.

  3. Загрузите свое расширение в Центр расширений, используя команду firebase ext:dev:upload .

  4. Перейдите в панель управления издателя в консоли Firebase, найдите только что загруженное расширение и нажмите «Опубликовать в Extensions Hub». Это запросит проверку у наших сотрудников, которая может занять несколько дней. В случае одобрения расширение будет опубликовано в Extensions Hub. В случае отклонения вы получите сообщение с объяснением причины; после этого вы сможете устранить выявленные проблемы и повторно отправить расширение на проверку.