Переназначьте код Cloud Functions как расширение Firebase.

1. Прежде чем начать

Расширение Firebase выполняет определенную задачу или набор задач в ответ на HTTP-запросы или события, запускаемые другими продуктами Firebase и Google, такими как Firebase Cloud Messaging, Cloud Firestore или Pub/Sub.

Что вы построите

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

Расширение geohash отображается в консоли Firebase.

Что вы узнаете

  • Как взять существующий код Cloud Functions и превратить его в распространяемое расширение Firebase
  • Как настроить файл extension.yaml
  • Как хранить конфиденциальные строки (ключи API) в расширении
  • Как предоставить разработчикам расширения возможность настраивать его в соответствии со своими потребностями?
  • Как протестировать и развернуть расширение

Что вам понадобится

  • Firebase CLI (установка и вход в систему)
  • Аккаунт Google, например, аккаунт Gmail.
  • Node.js и npm
  • Ваша любимая среда разработки

2. Настройка

Получите код

Все необходимое для этого расширения находится в репозитории GitHub. Чтобы начать, скачайте код и откройте его в вашей любимой среде разработки.

  1. Распакуйте загруженный zip-файл.
  2. Для установки необходимых зависимостей откройте терминал в каталоге functions и выполните команду npm install .

Настройка Firebase

В этом практическом занятии настоятельно рекомендуется использовать эмуляторы Firebase. Если вы хотите попробовать разработку расширений с реальным проектом Firebase, см. раздел «Создание проекта Firebase» . В этом практическом занятии используются Cloud Functions, поэтому, если вы используете реальный проект Firebase вместо эмуляторов, вам необходимо перейти на тарифный план Blaze .

Хотите перейти к следующему шагу?

Вы можете скачать готовую версию Codelab. Если у вас возникнут трудности или вы захотите посмотреть, как выглядит готовое расширение, загляните в ветку codelab-end репозитория GitHub или скачайте готовый zip-архив.

3. Проверьте код.

  • Откройте файл index.ts из архива. Обратите внимание, что он содержит два объявления облачных функций.

Что делают эти функции?

Эти демонстрационные функции используются для геохеширования. Они принимают пару координат и преобразуют их в формат, оптимизированный для геозапросов в Firestore. Функции имитируют использование вызова API, чтобы вы могли узнать больше об обработке конфиденциальных типов данных в расширениях. Для получения дополнительной информации см. документацию по выполнению геозапросов к данным в Firestore .

Функциональные константы

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

index.ts

import {firestore} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {GeoHashService, ResultStatusCode} from "./fake-geohash-service";
import {onCall} from "firebase-functions/v1/https";
import {fieldValueExists} from "./utils";

const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

initializeApp();

const service = new GeoHashService(apiKey);

Триггер Firestore

Первая функция в файле index.ts выглядит следующим образом:

index.ts

export const locationUpdate = firestore.document(documentPath)
  .onWrite((change) => {
    // item deleted
    if (change.after == null) {
      return 0;
    }
    // double check that both values exist for computation
    if (
      !fieldValueExists(change.after.data(), xField) ||
      !fieldValueExists(change.after.data(), yField)
    ) {
      return 0;
    }
    const x: number = change.after.data()![xField];
    const y: number = change.after.data()![yField];
    const hash = service.convertToHash(x, y);
    // This is to check whether the hash value has changed. If
    // it hasn't, you don't want to write to the document again as it
    // would create a recursive write loop.
    if (fieldValueExists(change.after.data(), outputField)
      && change.after.data()![outputField] == hash) {
      return 0;
    }
    return change.after.ref
      .update(
        {
          [outputField]: hash.hash,
        }
      );
  });

Эта функция является триггером Firestore . Когда в базе данных происходит событие записи, функция реагирует на него, ища поля xv и yv , и, если оба поля существуют, вычисляет геохеш и записывает результат в указанное место сохранения документа. Входной документ определяется константой users/{uid} , что означает, что функция считывает каждый документ, записанный в коллекцию users/ а затем вычисляет геохеш для этих документов. Затем она выводит хеш в поле хеша в том же документе.

Вызываемые функции

Следующая функция в файле index.ts выглядит следующим образом:

index.ts

export const callableHash = onCall((data, context) => {
  if (context.auth == undefined) {
    return {error: "Only authorized users are allowed to call this endpoint"};
  }
  const x = data[xField];
  const y = data[yField];
  if (x == undefined || y == undefined) {
    return {error: "Either x or y parameter was not declared"};
  }
  const result = service.convertToHash(x, y);
  if (result.status != ResultStatusCode.ok) {
    return {error: `Something went wrong ${result.message}`};
  }
  return {result: result.hash};
});

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

4. Создайте файл extension.yaml.

Теперь, когда вы знаете, что делает код Cloud Functions в вашем расширении, вы готовы упаковать его для распространения. Каждое расширение Firebase поставляется с файлом extension.yaml , который описывает, что делает расширение и как оно себя ведет.

Файл extension.yaml должен содержать некоторую начальную метаданную о вашем расширении. Каждый из следующих шагов поможет вам понять, что означают все поля и зачем они вам нужны.

  1. Создайте файл extension.yaml в корневом каталоге проекта, который вы скачали ранее. Для начала добавьте следующее:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Название расширения используется в качестве основы для идентификатора экземпляра расширения (пользователи могут установить несколько экземпляров расширения, каждый со своим идентификатором). Затем Firebase генерирует имена учетных записей служб расширения и ресурсов, специфичных для расширения, используя этот идентификатор экземпляра. Номер версии указывает версию вашего расширения. Он должен соответствовать семантическому версионированию , и вам необходимо обновлять его всякий раз, когда вы вносите изменения в функциональность расширения. Версия спецификации расширения используется для определения того, какой спецификации расширений Firebase следует придерживаться; в данном случае используется v1beta .

  1. Добавьте в YAML-файл несколько удобных для пользователя деталей:
...

displayName: Latitude and longitude to GeoHash converter
description: A converter for changing your Latitude and longitude coordinates to geohashes.

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

Расширение Geohash Converter, как видно на extensions.dev

  1. Укажите лицензию на код в вашем расширении.
...

license: Apache-2.0  # The license you want for the extension
  1. Укажите, кто разработал расширение и требуется ли оплата для его установки:
...

author:
  authorName: AUTHOR_NAME
  url: https://github.com/Firebase

billingRequired: true

Раздел author используется для того, чтобы сообщить пользователям, к кому обратиться в случае возникновения проблем с расширением или необходимости получения дополнительной информации о нем. Параметр billingRequired является обязательным и должен быть установлен в true поскольку все расширения используют Cloud Functions, для работы которых требуется тарифный план Blaze.

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

5. Преобразуйте код Cloud Functions в ресурс Extensions.

Ресурс расширения — это элемент, который Firebase создает в проекте во время установки расширения. Затем расширение владеет этими ресурсами и имеет определенную учетную запись службы, которая работает с ними. В этом проекте такими ресурсами являются облачные функции (Cloud Functions), которые должны быть определены в файле extension.yaml поскольку расширение не будет автоматически создавать ресурсы из кода в папке functions. Если ваши облачные функции не объявлены явно как ресурс, они не могут быть развернуты при развертывании расширения.

Место развертывания, определяемое пользователем

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

extension.yaml

Теперь вы готовы написать конфигурацию для ресурса функции.

  1. В файле extension.yaml создайте объект ресурса для функции locationUpdate . Добавьте в файл extension.yaml следующее:
resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}

Вы задаёте name как имя функции, определённое в файле index.ts проекта. Вы указываете type развертываемой функции, который на данный момент всегда должен быть firebaseextensions.v1beta.function . Затем вы определяете properties этой функции. Первое свойство, которое вы определяете, — это eventTrigger , связанный с этой функцией. Чтобы соответствовать тому, что в настоящее время поддерживает расширение, вы используете eventType из providers/cloud.firestore/eventTypes/document.write , который находится в документации по записи облачных функций для вашего расширения . Вы определяете resource как местоположение документов. Поскольку ваша текущая цель — воспроизвести то, что существует в коде, путь к документу прослушивает users/{uid} , а перед ним указывается местоположение базы данных по умолчанию.

  1. Расширению необходимы права на чтение и запись в базу данных Firestore. В самом конце файла extension.yaml укажите роли IAM, к которым расширение должно иметь доступ для работы с базой данных в проекте Firebase разработчика.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Роль datastore.user берется из списка поддерживаемых ролей IAM для расширений . Поскольку расширение будет осуществлять чтение и запись, роль datastore.user здесь вполне уместна.

  1. Необходимо также добавить вызываемую функцию. В файле extension.yaml создайте новый ресурс в свойстве resources. Эти свойства специфичны для вызываемой функции:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

Хотя в предыдущем примере использовался eventTrigger , здесь применяется httpsTrigger , который охватывает как вызываемые функции, так и функции HTTPS.

Проверка кода

Это потребовало много настроек, чтобы ваш extension.yaml соответствовал всему коду в вашем файле index.ts . Вот как должен выглядеть готовый файл extension.yaml на данный момент:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Проверка статуса

На этом этапе у вас уже настроены основные функциональные компоненты расширения, так что вы можете протестировать его с помощью эмуляторов Firebase!

  1. Если вы еще этого не сделали, вызовите команду npm run build в папке `functions` загруженного проекта расширений.
  2. Создайте новую директорию в вашей системе и подключите её к вашему проекту Firebase с помощью firebase init .
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
    This command creates a `firebase.json` file in the directory. In the following steps, you push the configuration specified in this file to Firebase.
  1. В той же директории выполните команду firebase ext:install . Замените /path/to/extension на абсолютный путь к директории, содержащей ваш файл extension.yaml .
firebase ext:install /path/to/extension
    This command does two things:
  • Программа запрашивает у вас конфигурацию для экземпляра расширения и создает файл *.env , содержащий информацию о конфигурации этого экземпляра.
  • Эта команда добавляет экземпляр расширения в раздел extensions вашего firebase.json . Она служит в качестве сопоставления идентификатора экземпляра с версией расширения.
  • Поскольку вы развертываете проект локально, вы можете указать, что хотите использовать локальный файл, а не менеджер секретов Google Cloud.

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

  1. Запустите эмуляторы Firebase с новой конфигурацией:
firebase emulators:start
  1. После запуска emulators:start перейдите на вкладку Firestore в веб-интерфейсе эмуляторов.
  2. Добавьте в коллекцию users документ с числовым полем xv и числовым полем yv .

Диалоговое окно, отображаемое в эмуляторах Firebase для запуска коллекции с идентификатором, содержащим фразу.

  1. Если установка расширения прошла успешно, оно создаст в документе новое поле с именем hash .

Коллекция пользователей, содержащая документ пользователя с полями xv, yv и хешем.

Наведите порядок, чтобы избежать конфликтов.

  • После завершения тестирования удалите расширение — вы собираетесь обновить код расширения и не хотите, чтобы оно конфликтовало с текущим расширением в дальнейшем.

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

firebase ext:uninstall geohash-ext

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

6. Сделайте расширение настраиваемым пользователем.

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

Определите основные параметры в файле extension.yaml

Начните с преобразования элементов, для которых у разработчиков может быть собственная конфигурация. Первыми следует преобразовать параметры XFIELD и YFIELD .

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

extension.yaml

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If no value is specified, the extension searches for
      field 'xv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      value. If no value is specified, the extension searches for
      field 'yv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  • Параметр `param` задаёт имя таким образом, чтобы оно было видно вам, разработчику расширения. Используйте это значение позже при указании значений параметров.
  • Параметр `label` — это удобочитаемый идентификатор, позволяющий разработчику понять, для чего он нужен.
  • Функция `description` дает подробное описание значения. Поскольку она поддерживает разметку Markdown, она может содержать ссылки на дополнительную документацию или выделять слова, которые могут быть важны для разработчика.
  • Тип определяет механизм ввода, с помощью которого пользователь будет задавать значение параметра. Существует множество типов, включая string , select , multiSelect , selectResource и secret . Чтобы узнать больше о каждом из этих вариантов, см. документацию .
  • Функция validationRegex ограничивает вводимые разработчиком данные определенным значением регулярного выражения (в примере оно основано на простых рекомендациях по именованию полей, которые можно найти здесь ); и если это не удается...
  • Функция validationErrorMessage оповещает разработчика о значении ошибки.
  • Значение по умолчанию — это то, какое значение было бы, если бы разработчик не ввел никакого текста.
  • Обязательный ввод означает, что разработчику не требуется вводить какой-либо текст.
  • Функция immutable позволяет разработчику обновлять это расширение и изменять это значение. В этом случае разработчик должен иметь возможность изменять имена полей по мере изменения своих требований.
  • Пример дает представление о том, как может выглядеть допустимый ввод.

Это было непросто понять!

  1. Перед добавлением специального параметра вам необходимо добавить еще три параметра в файл extension.yaml .
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has received a value, it notifies the extension to
      calculate a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash

Определите конфиденциальные параметры

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

  • В файл extension.yaml добавьте следующий код:

extension.yaml

  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

Обновите атрибуты resource , чтобы использовать параметры.

Как уже упоминалось ранее, ресурс (а не функция) определяет, как осуществляется наблюдение за ресурсом, поэтому ресурс locationUpdate необходимо обновить, чтобы использовать новый параметр.

  • В файл extension.yaml добавьте следующий код:

extension.yaml

## Change from this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}]

## To this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}

Проверьте файл extension.yaml

  • Проверьте файл extension.yaml . Он должен выглядеть примерно так:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want to use for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If you don't provide a value for this field, the extension will use 'xv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      Value. If you don't provide a value for this field, the extension will use 'yv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has been modified, it notifies the extension to
      compute a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash
  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Доступ к параметрам в коде

Теперь, когда все параметры настроены в файле extension.yaml , добавьте их в файл index.ts .

  • В файле index.ts замените значения по умолчанию на process.env.PARAMETER_NAME , что позволит получить соответствующие значения параметров и заполнить ими код функции, развернутой в проекте Firebase разработчика.

index.ts

// Replace this:
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

// with this:
const documentPath = process.env.INPUTPATH!; // this value is ignored since its read from the resource
const xField = process.env.XFIELD!;
const yField = process.env.YFIELD!;
const apiKey = process.env.APIKEY!;
const outputField = process.env.OUTPUTFIELD!;

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

7. Создайте пользовательскую документацию.

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

  1. Для начала создайте файл PREINSTALL.md , который используется для описания функциональности, любых предварительных условий для установки и возможных последствий для выставления счетов.

PREINSTALL.md

Use this extension to automatically convert documents with a latitude and
longitude to a geohash in your database. Additionally, this extension includes a callable function that allows users to make one-time calls
to convert an x,y coordinate into a geohash.

Geohashing is supported for latitudes between 90 and -90 and longitudes
between 180 and -180.

#### Third Party API Key

This extension uses a fictitious third-party API for calculating the
geohash. You need to supply your own API keys. (Since it's fictitious,
you can use 1234567890 as an API key).

#### Additional setup

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

After installing this extension, you'll need to:

- Update your client code to point to the callable geohash function if you
want to perform arbitrary geohashes.

Detailed information for these post-installation tasks are provided after
you install this extension.

#### 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:
 - Cloud Firestore
 - Cloud Functions (Node.js 16+ runtime. [See
FAQs](https://firebase.google.com/docs/functions/faq-and-troubleshooting#extensions-pricing))
 - [Cloud Secret Manager](https://cloud.google.com/secret-manager/pricing)
  1. Чтобы сэкономить время на написании файла README.md для этого проекта, используйте удобный метод:
firebase ext:info . --markdown > README.md

Это объединяет содержимое вашего файла PREINSTALL.md и дополнительные сведения о вашем расширении из файла extension.yaml .

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

  1. Создайте файл POSTINSTALL.md и добавьте в него следующую информацию, необходимую после установки:

POSTINSTALL.md

Congratulations on installing the geohash extension!

#### Function information

* **Firestore Trigger** - ${function:locationUpdate.name} was installed
and is invoked when both an x field (${param:XFIELD}) and y field
(${param:YFIELD}) contain a value.

* **Callable Trigger** - ${function:callableHash.name} was installed and
can be invoked by writing the following client code:
 ```javascript
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const geoHash = httpsCallable(functions, '${function:callableHash.name}');
geoHash({ ${param:XFIELD}: -122.0840, ${param:YFIELD}: 37.4221 })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const error = data.error;
    if (error != null) {
        console.error(`callable error : ${error}`);
    }
    const result = data.result;
    console.log(result);
  });

Мониторинг

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

The output rendering looks something like this when it's deployed:

<img src="img/82b54a5c6ca34b3c.png" alt="A preview of the latitude and longitude geohash converter extension in the firebase console"  width="957.00" />


## Test the extension with the full configuration
Duration: 03:00


It's time to make sure that the user-configurable extension is working the way it is intended.

* Change into the functions folder and ensure that the latest compiled version of the extensions exists. In the extensions project functions directory, call:

```console
npm run build

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

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

Установите и протестируйте с помощью эмуляторов Firebase.

  1. Создайте новую директорию в вашей системе и подключите её к вашему проекту Firebase с помощью firebase init .
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Из этой директории запустите firebase ext:install для установки расширения. Замените /path/to/extension на абсолютный путь к директории, содержащей ваш файл extension.yaml . Это запустит процесс установки вашего расширения и создаст файл .env , содержащий ваши конфигурации, прежде чем отправить конфигурацию в Firebase или на эмуляторы.
firebase ext:install /path/to/extension
  • Поскольку вы развертываете проект локально, укажите, что хотите использовать локальный файл, а не менеджер секретов Google Cloud.

da928c65ffa8ce15.png

  1. Запустите локальный набор эмуляторов:
firebase emulators:start

Установите и протестируйте с реальным проектом Firebase.

Вы можете установить расширение в реальный проект Firebase. Для тестирования рекомендуется использовать тестовый проект. Используйте этот алгоритм тестирования, если хотите проверить весь процесс работы расширения или если триггер вашего расширения еще не поддерживается набором эмуляторов Firebase (см. опцию эмулятора расширений ). В настоящее время эмуляторы поддерживают функции, запускаемые HTTP-запросами, и функции, запускаемые фоновыми событиями, для Cloud Firestore, Realtime Database и Pub/Sub.

  1. Создайте новую директорию в вашей системе и подключите её к вашему проекту Firebase с помощью firebase init .
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Затем из этой директории запустите firebase ext:install для установки расширения. Замените /path/to/extension на абсолютный путь к директории, содержащей ваш файл extension.yaml . Это запустит процесс установки вашего расширения и создаст файл .env , содержащий ваши конфигурации, прежде чем отправить конфигурацию в Firebase или на эмуляторы.
firebase ext:install /path/to/extension
  • Поскольку вы хотите развернуть приложение непосредственно в Firebase и использовать Google Cloud Secret Manager, вам необходимо активировать API Secret Manager перед установкой расширения.
  1. Разверните приложение в свой проект Firebase.
firebase deploy

Протестируйте расширение

  1. После выполнения firebase deploy или firebase emulators:start перейдите на вкладку `Firestore` в консоли Firebase или в веб-представлении эмуляторов, в зависимости от ситуации.
  2. Добавьте документ в коллекцию, указанную полями x и y . В этом случае обновленные документы находятся по адресу u/{uid} где поле x равно xv , а поле y равно yv .

Экран эмуляторов Firebase для добавления записи Firestore.

  1. Если установка расширения прошла успешно, после сохранения двух полей в документе будет создано новое поле с именем hash .

Скриншот базы данных Firestore из эмулятора, показывающий добавленный хеш.

8. Поздравляем!

Вы успешно преобразовали свою первую облачную функцию в расширение Firebase!

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

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

Что дальше?