Z tego przewodnika dowiesz się, jak przeprowadzić migrację rozszerzeń z wycofanego środowiska Firebase Extensions do funkcji, którą użytkownicy mogą zainstalować i wdrożyć w swoim środowisku Cloud Functions dla Firebase (2 generacji) bazy kodu.
Jest to zalecana ścieżka migracji. Firebase będzie prowadzić listę rozszerzeń z oficjalnymi odpowiednikami w npm. Z tego przewodnika dowiesz się, jak utworzyć własne rozszerzenie.
W tym przewodniku jako przykładu używamy rozszerzenia Stream Firestore to BigQuery (firestore-bigquery-export). Każda sekcja kończy się przykładem, który pokazuje, jak wyglądało rozszerzenie przed migracją, a jak wygląda po niej w postaci pakietu @firebase/firestore-bigquery-export.
Zarejestruj się, aby uzyskać więcej informacji i pomoc w przenoszeniu rozszerzeń
Jeśli masz pytania dotyczące migracji z Firebase Extensions, możesz skontaktować się z nami pod adresem firebase-extensions-migrator-support-external@google.com. Będziemy też wysyłać e-maile do tej grupy, gdy będziemy aktualizować przewodnik o dodatkowe informacje na temat przygotowywania pakietów, testowania i dystrybuowania funkcji 2. generacji.
Aby dołączyć do tej grupy, wyślij wiadomość na adres firebase-extensions-migrator-support-external+subscribe@google.com. Otrzymasz e-maila z prośbą o potwierdzenie członkostwa. Musisz odpowiedzieć na tego e-maila, a nie klikać przycisku „Dołącz do tej grupy”.
Zanim zaczniesz
Aby przeprowadzić tę migrację, użyj tych funkcji Cloud Functions:
Konfiguracja sparametryzowana. Każdy parametr zadeklarowany w
extension.yamlstaje się zdefiniowanym parametrem w kodzie pakietu.Deklaratywne role uprawnień i wymagane interfejsy API. Każda rola zadeklarowana w
extension.yamlstaje się wywołaniemrequiresRole(...), a każdy interfejs API staje się wywołaniemrequiresAPI(...)w kodzie funkcji. Podczas wdrażania interfejs Firebase CLI przypisuje zadeklarowane role do zarządzanego konta usługi środowiska wykonawczego i włącza zadeklarowane interfejsy API w Twoim imieniu.Zdarzenia cyklu życia w przypadku Cloud Functions baz kodu. Cloud FunctionsBazy kodu obsługują teraz zdarzenia cyklu życia analogiczne do Firebase Extensions. Zadeklaruj konfigurację czasu instalacji i aktualizacji za pomocą elementów zaczepienia cyklu życia
afterFirstDeploy(...)iafterRedeploy(...). Zastępują onelifecycleEventszadeklarowane wextension.yaml.
Sprawdź rozszerzenie
Zacznij od inwentaryzacji rozszerzenia, czyli pełnej listy wszystkich elementów, które rozszerzenie deklaruje, dostarcza i dokumentuje. Dzięki temu każda funkcja będzie miała zdefiniowane miejsce docelowe w funkcji 2 generacji i nic nie zostanie utracone podczas migracji.
Sprawdź te elementy i zwróć uwagę na to, co znajdziesz:
extension.yaml, który deklaruje parametry, funkcje, zdarzenia, role IAM, wymagane interfejsy API, tajne klucze i punkty zaczepienia cyklu życia.functions/, który zawiera kod funkcji, zależności, konfigurację kompilacji, aktywatory i funkcje kolejki zadań.README.md,PREINSTALL.mdiPOSTINSTALL.md, które zawierają instrukcje konfiguracji, ostrzeżenia i informacje o płatnościach.scripts/, który zawiera narzędzia do importowania, uzupełniania, zarządzania tożsamością i dostępem, naprawy lub migracji, a także inne narzędzia dostarczane wraz z rozszerzeniem.
Następnie dla każdego elementu w extension.yaml zdecyduj, gdzie ma się on znaleźć w pakiecie npm:
Przekształć konfigurację użytkownika w parametry Cloud Functions (sekcja 5).
Przekształć obiekty tajne w Cloud Functions obiekty tajne (sekcja 6).
Przekształć role uprawnień w deklaracje
requiresRole(...)(sekcja 8).Przekształć wymagane interfejsy API Google w deklaracje
requiresAPI(...), jeśli jest to odpowiednie (sekcja 8).Przekształć wywołania instalacji i aktualizacji w deklaracje
afterFirstDeploy(...)iafterRedeploy(...)(sekcja 8).
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Odczytanie plików firestore-bigquery-export/extension.yaml i functions/ daje ten spis:
| W pliku extension.yaml | Liczba / wartość | Gdzie trafiają dane |
|---|---|---|
| parametry | 25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) | Cloud Functions parametry (sekcja 5) |
| interfejsy API | bigquery.googleapis.com | requiresAPI(...) (sekcja 7) |
| role | bigquery.dataEditor, datastore.user, bigquery.user | requiresRole(...) (sekcja 7) |
| zasoby | 1 aktywator zdarzeń (fsexportbigquery) + funkcje kolejki zadań (initBigQuerySync, setupBigQuerySync) | Funkcje wyeksportowanego pakietu (sekcja 3) |
| lifecycleEvents | onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync | afterFirstDeploy / afterRedeploy (sekcja 9) |
| scripts/ | import/ (backfill), gen-schema-view/ | zachowane jako skrypty (nie są tu brane pod uwagę), |
Rozszerzenie nie deklaruje żadnego typu: secret params, więc w sekcji 6 tego przewodnika nie ma niczego do migracji. Wywoływacz zdarzeń jest już 2 generacji, a tylko funkcje kolejki zadań są nadal 1 generacji (ma to znaczenie w sekcji 3).
Aktualizowanie pliku package.json
Zaktualizuj plik package.json rozszerzenia. Jeśli przenosisz jedno rozszerzenie, może to być katalog główny package.json. Jeśli przenosisz wiele rozszerzeń w jednym repozytorium, każde z nich powinno mieć własny pakiet.
Minimalne wersje pakietu SDK. Zadeklaruj firebase-functions >= 7.3 i firebase-admin >= 14.2.0 jako zależności. Zadeklaruj swoją wersję firebase-functions jako zależność równorzędną
{
"name": "<package-name>",
"version": "1.0.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
"engines": {
"node": ">=22"
},
"peerDependencies": {
"firebase-functions": "^7.3.0"
},
"dependencies": {
"firebase-functions": "^7.3.0",
"firebase-admin": "^14.2.0"
}
}
Zadeklaruj firebase-functions jako zależność równorzędną oprócz normalnej zależności, aby projekt Cloud Functions użytkowników miał tę samą wersję pakietu SDK, w której napisano Twoją bibliotekę.
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Przed. Plik functions/package.json rozszerzenia jest prywatny, zawiera identyfikator rozszerzenia i deklaruje firebase-functions jako bezpośrednią zależność:
{
"name": "firestore-bigquery-export",
"main": "lib/index.js",
"private": true,
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^6.3.2"
}
}
Po Pakiet do opublikowania: nazwa z zakresem, mapa eksportów i firebase-functions przeniesione do peerDependencies:
{
"name": "@firebase/firestore-bigquery-export",
"version": "0.1.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" }, },
"engines": { "node": ">=22" },
"peerDependencies": { "firebase-functions": "^7.3.0" },
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^7.3.0"
}
}
Uaktualnianie funkcji z 1 generacji do 2 generacji
Jeśli rozszerzenie nadal eksportuje funkcje 1 generacji, przekonwertuj każdą funkcję na jej odpowiednik 2 generacji. Zaimportuj moduły z firebase-functions/... i przekaż ustawienia środowiska wykonawczego w opcjach funkcji.
Możesz zminimalizować wysiłek związany z przepisywaniem kodu dzięki rozbijaniu struktury zdarzeń w przypadku poprawek w pakiecie SDK 2 generacji i uniknąć przepisywania logiki funkcji, ponieważ pakiet SDK 2 generacji udostępnia teraz parametry V1 jako pola w obiekcie zdarzenia, co pozwala używać rozbitych/nazwanych parametrów i nie zmieniać logiki biznesowej.
Przed. 1 generacja:
import * as functions from "firebase-functions/v1";
export const sync = functions.firestore
.document("{collectionId}/{documentId}")
.onWrite(async (change, context) => {
await handleWrite(change.before, change.after, context.params);
});
Po 2 generacja:
import { onDocumentWritten } from "firebase-functions/firestore";
export const syncV2 = onDocumentWritten(
{ document: "{collectionId}/{documentId}" },
async ({change,context}) =>
await handleWrite(change.before, change.after, context.params);
);
Pełną listę różnic między funkcjami 1 generacji i 2 generacji znajdziesz w Cloud Functionsporównaniu wersji.
Konwertowanie parametrów i obiektów tajnych rozszerzenia
Parametry konwersji
Każdy parametr zadeklarowany w extension.yaml staje się parametrem Cloud Functions.
Konwertuj odczyty bezpośrednie środowiska:
const collectionPath = process.env.COLLECTION_PATH;
w parametrach Cloud Functions:
import { defineString } from "firebase-functions/params";
import { onDocumentWritten} from "firebase-functions/firestore";
const collectionPath = defineString("COLLECTION_PATH");
// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
{ document: collectionPath },
async (event) => {
// Call .value() to read the string inside a handler
const path = collectionPath.value();
await handleWrite(path, event);
}
);
Użyj collectionPath.value(), aby odczytać ciąg w obsłudze, i collectionPath bezpośrednio w miejscu, w którym oczekiwany jest symbol zastępczy, np. w ścieżce wyzwalacza funkcji.
Interfejs Firebase CLI wykrywa parametry i odczytuje ich wartości z plików .env lub .env.projectId albo wyświetla użytkownikom prośby o podanie tych wartości podczas wdrażania. Zachowaj te same nazwy parametrów, aby wartości z istniejącej instalacji zostały przeniesione.
Ważne jest, aby nie zmieniać nazw parametrów zadeklarowanych w kodzie. Migracja rozszerzenia automatycznie zachowa dotychczasową wartość parametru użytkownika, ale tylko wtedy, gdy nazwy pozostaną bez zmian.
Przykład: przesyłanie strumieniowe Firestore do BigQuery
Przed Parametr zadeklarowany w extension.yaml, odczytany jako zmienna środowiskowa w config.ts:
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
Po Jeden defineString; interfejs wiersza poleceń go wykrywa i odczytuje z .env:
// src/config.ts
import { defineString } from "firebase-functions/params";
collectionPath: defineString("COLLECTION_PATH", {
label: "Collection path",
// We now support "nonEmpty: true" to ensure a value other than the empty string
// is entered, analagous to "required: true" in extensions.yaml
input: { text: { nonEmpty: true} }
}),
Nazwa parametru nie uległa zmianie, więc istniejący plik .env nadal działa.
Konwertowanie obiektów tajnych
W pliku extension.yaml deklarujesz obiekty tajne za pomocą typu: secret. Środowisko wykonawcze rozszerzeń przechowuje je i wiąże, dzięki czemu kod rozszerzenia może bezpośrednio odczytywać process.env.PARAM_NAME. W typowej bazie kodu Cloud Functions deklarujesz i wiążesz każdy klucz tajny w sposób jawny:
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
Po przeniesieniu rozszerzenia do pakietu lub zestawu npm odwołania do kluczy tajnych będą zarządzane w pliku .env użytkownika. Ważne jest, aby nie zmieniać nazw kluczy tajnych zadeklarowanych w kodzie w ogóle. Podczas migracji zostaną przeniesione odpowiednie dane logowania użytkowników.
Przykład: wysyłanie e-maila z Cloud Firestore
Przed. Zmienne środowiskowe MAIL_COLLECTION i SMTP_PASSWORD są odczytywane jako surowe zmienne środowiskowe w pliku config.ts:
# extension.yaml
- param: MAIL_COLLECTION
label: Email documents collection
type: string
default: mail
required: true
- param: SMTP_PASSWORD
label: SMTP password
type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,
Po Jeden plik defineString i jeden plik defineSecret; interfejs wiersza poleceń wykrywa oba pliki i odczytuje dane z pliku .env
import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const mailCollection = defineString("MAIL_COLLECTION",{ label: "Email documents collection",
default: "mail"
});
const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });
export const processQueue = onDocumentWritten(
{ document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
async (event) => {
const collection = mailCollection.value();
const password = smtpPassword.value();
// ...
}
);
Migracja wewnętrznych wywołań kolejki zadań
Niektóre rozszerzenia umieszczają zadania w własnych kolejkach zadań z poziomu kodu funkcji, używając Firebase Admin SDK. Różni się to od otrzymywania wysłanego zadania (omówionego w sekcjach Uaktualnianie funkcji i Konwertowanie wywołań zwrotnych cyklu życia). W tym przypadku kod jest producentem, który wywołuje queue.enqueue(...).
W poprzednich wersjach Admin SDK rozszerzenia musiały przekazywać własny identyfikator instancji rozszerzenia jako drugi parametr, aby kierować funkcję kolejki zadań w tym samym rozszerzeniu. Od wersji 14.2.0 pakietu `firebase-admin` nie jest to wymagane ani zalecane. Interfejs Task Queue API będzie teraz domyślnie kierować zadania do kolejek zadań w tym samym kontekście (np. rozszerzeniu). Możesz bezpiecznie usunąć ten parametr z kodu zarówno w rozszerzeniu, jak i w funkcjach autonomicznych. Usunięcie tego parametru zapewnia przenośność i zgodność z przyszłymi wersjami.
Wszystkie inne elementy wywołania enqueue, czyli ścieżka zasobu locations/region/functions/name, ładunek zadania i logika ponawiania, pozostają bez zmian.
Więcej informacji o kolejkowaniu funkcji za pomocą Cloud Tasks znajdziesz na stronie /docs/functions/task-functions.
Przed. Rozszerzenie 1 generacji
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${config.location}/functions/syncBigQuery`,
process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);
Po Rozszerzenie 2 generacji
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`);
await queue.enqueue(taskData);
Jeśli wywołanie kolejki dotyczy bazy kodu z prefiksem, wykryta nazwa funkcji również będzie miała prefiks (np. orders-syncBigQuery).
Deklarowanie wymaganych interfejsów API i ról uprawnień
Przenieś wymagania rozszerzenia dotyczące IAM i interfejsu API z pliku extension.yaml do:
import { requiresAPI, requiresRole } from "firebase-functions"
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
W przypadku deklaratywnego zabezpieczenia interfejs Firebase CLI tworzy lub aktualizuje zarządzane konto usługi środowiska wykonawczego dla bazy kodu i przyznaje mu sumę wszystkich zadeklarowanych ról. Poinformuj użytkowników, że wszystkie funkcje w bazie kodu działają z tymi rolami, chyba że końcowy interfejs API obsługuje węższy model.
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Przed. Zadeklarowane w pliku extension.yaml; środowisko wykonawcze rozszerzeń włączyło interfejs API i przyznało role zarządzanemu kontu:
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
Po Zadeklarowane w kodzie za pomocą funkcji requiresAPI i requiresRole:
import { requiresAPI, requiresRole } from "firebase-functions/";
requiresAPI("bigquery.googleapis.com",
"Needed to write changelog rows and views");
requiresRole("roles/biguqery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");
Konwertowanie ciekawostek dotyczących cyklu życia
Jeśli rozszerzenie wywołuje na przykład getExtensions().runtime()setProcessingState lub setFatalError, usuń te wywołania, ponieważ w przypadku wywołania z normalnie wdrożonej funkcji 2 generacji spowodują one błąd. Stan cyklu życia jest teraz określany przez afterFirstDeploy i afterRedeploy, w przypadku których śledzenie stanu nie jest używane.
Firebase Extensions może uruchamiać konfigurację, gdy użytkownik zainstaluje, zaktualizuje lub ponownie skonfiguruje rozszerzenie. W pakiecie npm zadeklaruj w kodzie równoważne działania związane z cyklem życia.
Aby skonfigurować urządzenie po raz pierwszy:
import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";
export const runInitialSetup = onTaskDispatched(async (request) => {
await initializeResources(request.data);
});
afterFirstDeploy({
task: {
function: "runInitialSetup",
body: {}
}
});
W przypadku aktualizacji konfiguracji lub kodu:
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
Zadbaj o to, aby działania związane z cyklem życia były idempotentne. Jeśli wysyłanie lub wykonywanie się nie powiedzie, użytkownicy mogą ręcznie ponownie uruchomić te skrypty:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Przed. lifecycleEvents w extension.yaml, czynnik kosztowy: środowisko wykonawcze rozszerzeń:
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
Po Zadeklarowane w kodzie; zadanie zapewnia BigQuery przy pierwszym wdrożeniu:
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
Aprowizacja jest idempotentna, więc ponowne uruchomienie powoduje uzgodnienie zbioru danych, tabeli i widoków. Użytkownicy mogą ponownie uruchomić funkcję ręcznie za pomocą polecenia firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME.
Konfigurowanie dokumentów dla użytkowników
Napisz
README, w którym wyjaśnisz co najmniej:Wartości
.envwymagane przez pakiet.obiekty tajne wymagane przez pakiet oraz sposób przenoszenia istniejących wartości obiektów tajnych.
Role uprawnień zadeklarowane w pakiecie za pomocą
requiresRole(...).Interfejsy API Google, które pakiet włącza lub których wymaga.
Punkty zaczepienia cyklu życia zadeklarowane przez pakiet i sposób ich ponownego ręcznego uruchomienia.
Uwagi dotyczące płatności.
Co się zmieniło w porównaniu z oryginalnym rozszerzeniem.
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Plik README pakietu zawiera konkretną tabelę „Co się zmieniło”:
| Potencjalny problem | Jako rozszerzenie | As @firebase/firestore-bigquery-export |
|---|---|---|
| Konfiguracja | Parametry rozszerzenia | Parametry funkcji za pomocą pliku .env |
| Uprawnienia | Przyznane przez rozszerzenia | requiresRole(...), zastosowane podczas wdrażania |
| Udostępniam | Zadanie cyklu życia według rozszerzeń | Zadanie afterFirstDeploy / afterRedeploy |
| Nazwy funkcji | ext-instanceId-fsexportbigquery | fsexportbigquery (opcjonalnie z prefiksem) |
Testowanie funkcji 2 generacji
Powinna być teraz dostępna funkcja 2 generacji, która po wdrożeniu będzie działać identycznie jak nowa instalacja rozszerzenia. Ostatnim krokiem jest sprawdzenie i rozwiązanie wszelkich problemów, które mogły się pojawić po drodze.
Upewnij się, że używasz
firebase-tools
>= w wersji 15.25.1 i wdrażasz przekonwertowaną funkcję 2 generacji w projekcie testowym
z odpowiednimi zasobami, aby przetestować jej działanie. Jeśli masz już projekt testowy skonfigurowany na potrzeby testowania rozszerzenia, użyj polecenia:
firebase deploy --only functions
Po wpisaniu tego polecenia wypełnij kreator, podając wartości parametrów w taki sam sposób, jak w przypadku formularza instalacji w konsoli Firebase dla rozszerzenia.
Przykład: przesyłanie strumieniowe danych z Firestore do BigQuery
Sprawdzamy synchronizację Cloud Firestore–BigQuery na całej długości:
- W konsoli Cloud Firestore utwórz kolekcję, którą ustawisz jako COLLECTION_PATH (użytkownicy), jeśli jeszcze nie istnieje.
- Utwórz dokument o nazwie bigquery-mirror-test zawierający dowolne pola z dowolnymi wartościami.
- W konsoli BigQuery utwórz zapytanie do tabeli z surowymi danymi dziennika zmian. Powinien zawierać jeden wiersz rejestrujący utworzenie dokumentu:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
- Wyślij zapytanie o najnowszą wersję, która powinna zwrócić najnowsze zdarzenie zmiany dla jedynego dokumentu:
bigquery-mirror-test
SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
- Usuń dokument
bigquery-mirror-testw Cloud Firestore. Znika z widoku najnowszych zmian, a do tabeli surowego dziennika zmian dodawane jest zdarzenieDELETE.
Pełną historię pojedynczego dokumentu możesz sprawdzić za pomocą tych narzędzi:
SELECT *
FROM `PROJECT_ID.analytics.users_raw_changelog`
WHERE document_name = "bigquery-mirror-test"
ORDER BY timestamp ASC
Różnice w porównaniu z testowaniem rozszerzenia:
- Reguła jest wdrażana jako
fsexportbigquery(opcjonalnie z prefiksem kodu), a nieext-<instanceId>-fsexportbigquery. Poszukaj tej nazwy w panelu Cloud Functions i dziennikach. - Kod będzie teraz działać w Firebase Local Emulator Suite jak zwykłe funkcje. Wartości parametrów, które mają być używane w emulatorze, możesz ustawić za pomocą polecenia
.env.local. Możesz też testować jednostkowo kod za pomocą pakietu SDK firebase-functions-test zgodnie z opisem w sekcji Testowanie jednostkoweCloud Functions. - Inicjowanie nie jest już oparte na środowisku wykonawczym rozszerzeń. Jeśli po wdrożeniu brakuje tabeli dziennika zmian, ponownie uruchom ręcznie zadanie konfiguracji:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME. Zadanie jest idempotentne, więc ponowne jego uruchomienie spowoduje uzgodnienie zbioru danych, tabeli i widoków. - Wartości parametrów pochodzą z
.env, a nie z formularza instalacji, więc ponowne uruchomieniafirebase deploysą nieinteraktywne po zakończeniu.env.