Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update emulator usage of api to apiv2 #4573

Merged
merged 5 commits into from May 23, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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