Skip to content

Commit

Permalink
update emulator usage of api to apiv2 (#4573)
Browse files Browse the repository at this point in the history
* update emulator usage of api to apiv2

* fix missing semicolon

* clean up clients

* fix exporting in storage emulator
  • Loading branch information
bkendall committed May 23, 2022
1 parent dcab2d6 commit f59ae25
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 103 deletions.
2 changes: 1 addition & 1 deletion scripts/triggers-end-to-end-tests/triggers/package.json
Expand Up @@ -3,7 +3,7 @@
"description": "Cloud Functions for Firebase",
"scripts": {},
"engines": {
"node": "12"
"node": "16"
},
"dependencies": {
"@google-cloud/pubsub": "^1.1.5",
Expand Down
2 changes: 1 addition & 1 deletion scripts/triggers-end-to-end-tests/v1/package.json
Expand Up @@ -3,7 +3,7 @@
"description": "Cloud Functions for Firebase",
"scripts": {},
"engines": {
"node": "12"
"node": "16"
},
"dependencies": {
"firebase-admin": "^9.3.0",
Expand Down
2 changes: 1 addition & 1 deletion scripts/triggers-end-to-end-tests/v2/package.json
Expand Up @@ -3,7 +3,7 @@
"description": "Cloud Functions for Firebase",
"scripts": {},
"engines": {
"node": "12"
"node": "16"
},
"dependencies": {
"firebase-admin": "^9.3.0",
Expand Down
12 changes: 7 additions & 5 deletions src/emulator/databaseEmulator.ts
Expand Up @@ -4,14 +4,14 @@ import * as fs from "fs";
import * as path from "path";
import * as http from "http";

import * as api from "../api";
import * as downloadableEmulators from "./downloadableEmulators";
import { EmulatorInfo, EmulatorInstance, Emulators } from "../emulator/types";
import { Constants } from "./constants";
import { EmulatorRegistry } from "./registry";
import { EmulatorLogger } from "./emulatorLogger";
import { FirebaseError } from "../error";
import * as parseBoltRules from "../parseBoltRules";
import { Client } from "../apiv2";

export interface DatabaseEmulatorArgs {
port?: number;
Expand Down Expand Up @@ -170,11 +170,13 @@ export class DatabaseEmulator implements EmulatorInstance {

const info = this.getInfo();
try {
await api.request("PUT", `/.settings/rules.json?ns=${instance}`, {
origin: `http://${EmulatorRegistry.getInfoHostString(info)}`,
const client = new Client({
urlPrefix: `http://${EmulatorRegistry.getInfoHostString(info)}`,
auth: false,
});
await client.put(`/.settings/rules.json`, content, {
headers: { Authorization: "Bearer owner" },
data: content,
json: false,
queryParams: { ns: instance },
});
} catch (e: any) {
// The body is already parsed as JSON
Expand Down
28 changes: 14 additions & 14 deletions src/emulator/firestoreEmulator.ts
Expand Up @@ -3,13 +3,13 @@ import * as fs from "fs";
import * as clc from "cli-color";
import * as path from "path";

import * as api from "../api";
import * as utils from "../utils";
import * as downloadableEmulators from "./downloadableEmulators";
import { EmulatorInfo, EmulatorInstance, Emulators, Severity } from "../emulator/types";
import { EmulatorRegistry } from "./registry";
import { Constants } from "./constants";
import { Issue } from "./types";
import { Client } from "../apiv2";

export interface FirestoreEmulatorArgs {
port?: number;
Expand Down Expand Up @@ -90,7 +90,7 @@ export class FirestoreEmulator implements EmulatorInstance {
return Emulators.FIRESTORE;
}

private updateRules(content: string): Promise<Issue[]> {
private async updateRules(content: string): Promise<Issue[]> {
const projectId = this.args.projectId;

const info = this.getInfo();
Expand All @@ -107,18 +107,18 @@ export class FirestoreEmulator implements EmulatorInstance {
},
};

return api
.request("PUT", `/emulator/v1/projects/${projectId}:securityRules`, {
origin: `http://${EmulatorRegistry.getInfoHostString(info)}`,
data: body,
})
.then((res) => {
if (res.body && res.body.issues) {
return res.body.issues as Issue[];
}

return [];
});
const client = new Client({
urlPrefix: `http://${EmulatorRegistry.getInfoHostString(info)}`,
auth: false,
});
const res = await client.put<any, { issues?: Issue[] }>(
`/emulator/v1/projects/${projectId}:securityRules`,
body
);
if (res.body && Array.isArray(res.body.issues)) {
return res.body.issues;
}
return [];
}

/**
Expand Down
80 changes: 37 additions & 43 deletions src/emulator/functionsEmulator.ts
Expand Up @@ -11,7 +11,6 @@ import { URL } from "url";
import { EventEmitter } from "events";

import { Account } from "../auth";
import * as api from "../api";
import { logger } from "../logger";
import { track } from "../track";
import { Constants } from "./constants";
Expand Down Expand Up @@ -68,6 +67,7 @@ import * as backend from "../deploy/functions/backend";
import * as functionsEnv from "../functions/env";
import { AUTH_BLOCKING_EVENTS, BEFORE_CREATE_EVENT } from "../functions/events/v1";
import { BlockingFunctionsConfig } from "../gcp/identityPlatform";
import { Client } from "../apiv2";

const EVENT_INVOKE = "functions:invoke";

Expand Down Expand Up @@ -694,16 +694,17 @@ export class FunctionsEmulator implements EmulatorInstance {
const path = `/identitytoolkit.googleapis.com/v2/projects/${this.getProjectId()}/config?updateMask=blockingFunctions`;

try {
await api.request("PATCH", path, {
origin: `http://${EmulatorRegistry.getInfoHostString(authEmu.getInfo())}`,
headers: {
Authorization: "Bearer owner",
},
data: {
blockingFunctions: this.blockingFunctionsConfig,
},
json: true,
const client = new Client({
urlPrefix: `http://${EmulatorRegistry.getInfoHostString(authEmu.getInfo())}`,
auth: false,
});
await client.patch(
path,
{ blockingFunctions: this.blockingFunctionsConfig },
{
headers: { Authorization: "Bearer owner" },
}
);
} catch (err) {
this.logger.log(
"WARN",
Expand All @@ -713,14 +714,14 @@ export class FunctionsEmulator implements EmulatorInstance {
}
}

addRealtimeDatabaseTrigger(
async addRealtimeDatabaseTrigger(
projectId: string,
key: string,
eventTrigger: EventTrigger
): Promise<boolean> {
const databaseEmu = EmulatorRegistry.get(Emulators.DATABASE);
if (!databaseEmu) {
return Promise.resolve(false);
return false;
}

const result: string[] | null = DATABASE_PATH_PATTERN.exec(eventTrigger.resource);
Expand All @@ -729,7 +730,7 @@ export class FunctionsEmulator implements EmulatorInstance {
"WARN",
`Event function "${key}" has malformed "resource" member. ` + `${eventTrigger.resource}`
);
return Promise.reject();
throw new FirebaseError(`Event function ${key} has malformed resource member`);
}

const instance = result[1];
Expand All @@ -752,25 +753,20 @@ export class FunctionsEmulator implements EmulatorInstance {
);
}

return api
.request("POST", setTriggersPath, {
origin: `http://${EmulatorRegistry.getInfoHostString(databaseEmu.getInfo())}`,
headers: {
Authorization: "Bearer owner",
},
data: bundle,
json: false,
})
.then(() => {
return true;
})
.catch((err) => {
this.logger.log("WARN", "Error adding Realtime Database function: " + err);
throw err;
});
const client = new Client({
urlPrefix: `http://${EmulatorRegistry.getInfoHostString(databaseEmu.getInfo())}`,
auth: false,
});
try {
await client.post(setTriggersPath, bundle, { headers: { Authorization: "Bearer owner" } });
} catch (err: any) {
this.logger.log("WARN", "Error adding Realtime Database function: " + err);
throw err;
}
return true;
}

addFirestoreTrigger(
async addFirestoreTrigger(
projectId: string,
key: string,
eventTrigger: EventTrigger
Expand All @@ -788,19 +784,17 @@ export class FunctionsEmulator implements EmulatorInstance {
});
logger.debug(`addFirestoreTrigger`, JSON.stringify(bundle));

return api
.request("PUT", `/emulator/v1/projects/${projectId}/triggers/${key}`, {
origin: `http://${EmulatorRegistry.getInfoHostString(firestoreEmu.getInfo())}`,
data: bundle,
json: false,
})
.then(() => {
return true;
})
.catch((err) => {
this.logger.log("WARN", "Error adding firestore function: " + err);
throw err;
});
const client = new Client({
urlPrefix: `http://${EmulatorRegistry.getInfoHostString(firestoreEmu.getInfo())}`,
auth: false,
});
try {
await client.put(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle);
} catch (err: any) {
this.logger.log("WARN", "Error adding firestore function: " + err);
throw err;
}
return true;
}

async addPubsubTrigger(
Expand Down
33 changes: 11 additions & 22 deletions src/emulator/hubClient.ts
@@ -1,6 +1,6 @@
import * as api from "../api";
import { EmulatorHub, Locator, GetEmulatorsResponse } from "./hub";
import { FirebaseError } from "../error";
import { Client } from "../apiv2";

export class EmulatorHubClient {
private locator: Locator | undefined;
Expand All @@ -13,31 +13,20 @@ export class EmulatorHubClient {
return this.locator !== undefined;
}

getStatus(): Promise<void> {
return api.request("GET", "/", {
origin: this.origin,
});
async getStatus(): Promise<void> {
const apiClient = new Client({ urlPrefix: this.origin, auth: false });
await apiClient.get("/");
}

getEmulators(): Promise<GetEmulatorsResponse> {
return api
.request("GET", EmulatorHub.PATH_EMULATORS, {
origin: this.origin,
json: true,
})
.then((res) => {
return res.body as GetEmulatorsResponse;
});
async getEmulators(): Promise<GetEmulatorsResponse> {
const apiClient = new Client({ urlPrefix: this.origin, auth: false });
const res = await apiClient.get<GetEmulatorsResponse>(EmulatorHub.PATH_EMULATORS);
return res.body;
}

postExport(path: string): Promise<void> {
return api.request("POST", EmulatorHub.PATH_EXPORT, {
origin: this.origin,
json: true,
data: {
path,
},
});
async postExport(path: string): Promise<void> {
const apiClient = new Client({ urlPrefix: this.origin, auth: false });
await apiClient.post(EmulatorHub.PATH_EXPORT, { path });
}

get origin(): string {
Expand Down
42 changes: 26 additions & 16 deletions src/emulator/hubExport.ts
Expand Up @@ -3,7 +3,6 @@ import * as fs from "fs";
import * as fse from "fs-extra";
import * as http from "http";

import * as api from "../api";
import { logger } from "../logger";
import { IMPORT_EXPORT_EMULATORS, Emulators, ALL_EMULATORS } from "./types";
import { EmulatorRegistry } from "./registry";
Expand All @@ -13,6 +12,7 @@ import { getDownloadDetails } from "./downloadableEmulators";
import { DatabaseEmulator } from "./databaseEmulator";
import { StorageEmulator } from "./storage";
import * as rimraf from "rimraf";
import { Client } from "../apiv2";

export interface FirestoreExportMetadata {
version: string;
Expand Down Expand Up @@ -133,29 +133,32 @@ export class HubExport {
export_name: metadata.firestore!!.path,
};

return api.request("POST", `/emulator/v1/projects/${this.projectId}:export`, {
origin: firestoreHost,
json: true,
data: firestoreExportBody,
});
const client = new Client({ urlPrefix: firestoreHost, auth: false });
await client.post(`/emulator/v1/projects/${this.projectId}:export`, firestoreExportBody);
}

private async exportDatabase(metadata: ExportMetadata): Promise<void> {
const databaseEmulator = EmulatorRegistry.get(Emulators.DATABASE) as DatabaseEmulator;
const databaseAddr = `http://${EmulatorRegistry.getInfoHostString(databaseEmulator.getInfo())}`;
const client = new Client({ urlPrefix: databaseAddr, auth: true });

// Get the list of namespaces
const inspectURL = `/.inspect/databases.json?ns=${this.projectId}`;
const inspectRes = await api.request("GET", inspectURL, { origin: databaseAddr, auth: true });
const inspectURL = `/.inspect/databases.json`;
const inspectRes = await client.get<Array<{ name: string }>>(inspectURL, {
queryParams: { ns: this.projectId },
});
const namespaces = inspectRes.body.map((instance: any) => instance.name);

// Check each one for actual data
const namespacesToExport: string[] = [];
for (const ns of namespaces) {
const checkDataPath = `/.json?ns=${ns}&shallow=true&limitToFirst=1`;
const checkDataRes = await api.request("GET", checkDataPath, {
origin: databaseAddr,
auth: true,
const checkDataPath = `/.json`;
const checkDataRes = await client.get(checkDataPath, {
queryParams: {
ns,
shallow: "true",
limitToFirst: 1,
},
});
if (checkDataRes.body !== null) {
namespacesToExport.push(ns);
Expand Down Expand Up @@ -244,11 +247,18 @@ export class HubExport {
path: storageExportPath,
};

return api.request("POST", "/internal/export", {
origin: storageHost,
json: true,
data: storageExportBody,
const client = new Client({ urlPrefix: storageHost, auth: false });
const res = await client.request({
method: "POST",
path: "/internal/export",
headers: { "Content-Type": "application/json" },
body: storageExportBody,
responseType: "stream",
resolveOnHTTPError: true,
});
if (res.status >= 400) {
throw new FirebaseError(`Failed to export storage: ${await res.response.text()}`);
}
}
}

Expand Down

0 comments on commit f59ae25

Please sign in to comment.