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

Add --disable-triggers flag to database write commands #5179

Merged
merged 6 commits into from Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,3 +2,4 @@
- Releases RTDB Emulator v4.11.0: Wire protocol update for `startAfter`, `endBefore`.
- Changes `superstatic` dependency to `v8`, addressing Hosting emulator issues on Windows.
- Fixes internal library that was not being correctly published.
- Adds `--disable-triggers` flag to RTDB write commands.
5 changes: 5 additions & 0 deletions src/commands/database-push.ts
Expand Up @@ -21,6 +21,7 @@ export const command = new Command("database:push <path> [infile]")
"--instance <instance>",
"use the database <instance>.firebaseio.com (if omitted, use default database instance)"
)
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
.before(requirePermissions, ["firebasedatabase.instances.update"])
.before(requireDatabaseInstance)
.before(populateInstanceDetails)
Expand All @@ -33,6 +34,9 @@ export const command = new Command("database:push <path> [infile]")
utils.stringToStream(options.data) || (infile ? fs.createReadStream(infile) : process.stdin);
const origin = realtimeOriginOrEmulatorOrCustomUrl(options.instanceDetails.databaseUrl);
const u = new URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
if (options.disableTriggers) {
u.searchParams.set("disableTriggers", "true");
}

if (!infile && !options.data) {
utils.explainStdin();
Expand All @@ -46,6 +50,7 @@ export const command = new Command("database:push <path> [infile]")
method: "POST",
path: u.pathname,
body: inStream,
queryParams: u.searchParams,
});
} catch (err: any) {
logger.debug(err);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/database-remove.ts
Expand Up @@ -17,6 +17,7 @@ export const command = new Command("database:remove <path>")
"--instance <instance>",
"use the database <instance>.firebaseio.com (if omitted, use default database instance)"
)
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
.before(requirePermissions, ["firebasedatabase.instances.update"])
.before(requireDatabaseInstance)
.before(populateInstanceDetails)
Expand All @@ -40,7 +41,7 @@ export const command = new Command("database:remove <path>")
return utils.reject("Command aborted.", { exit: 1 });
}

const removeOps = new DatabaseRemove(options.instance, path, origin);
const removeOps = new DatabaseRemove(options.instance, path, origin, !!options.disableTriggers);
await removeOps.execute();
utils.logSuccess("Data removed successfully");
});
5 changes: 5 additions & 0 deletions src/commands/database-set.ts
Expand Up @@ -23,6 +23,7 @@ export const command = new Command("database:set <path> [infile]")
"--instance <instance>",
"use the database <instance>.firebaseio.com (if omitted, use default database instance)"
)
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
.before(requirePermissions, ["firebasedatabase.instances.update"])
.before(requireDatabaseInstance)
.before(populateInstanceDetails)
Expand All @@ -34,6 +35,9 @@ export const command = new Command("database:set <path> [infile]")
const origin = realtimeOriginOrEmulatorOrCustomUrl(options.instanceDetails.databaseUrl);
const dbPath = utils.getDatabaseUrl(origin, options.instance, path);
const dbJsonURL = new URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
if (options.disableTriggers) {
dbJsonURL.searchParams.set("disableTriggers", "true");
}

const confirm = await promptOnce(
{
Expand Down Expand Up @@ -61,6 +65,7 @@ export const command = new Command("database:set <path> [infile]")
method: "PUT",
path: dbJsonURL.pathname,
body: inStream,
queryParams: dbJsonURL.searchParams,
});
} catch (err: any) {
logger.debug(err);
Expand Down
5 changes: 5 additions & 0 deletions src/commands/database-update.ts
Expand Up @@ -23,6 +23,7 @@ export const command = new Command("database:update <path> [infile]")
"--instance <instance>",
"use the database <instance>.firebaseio.com (if omitted, use default database instance)"
)
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
.before(requirePermissions, ["firebasedatabase.instances.update"])
.before(requireDatabaseInstance)
.before(populateInstanceDetails)
Expand Down Expand Up @@ -51,6 +52,9 @@ export const command = new Command("database:update <path> [infile]")
(infile && fs.createReadStream(infile)) ||
process.stdin;
const jsonUrl = new URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
if (options.disableTriggers) {
jsonUrl.searchParams.set("disableTriggers", "true");
}

if (!infile && !options.data) {
utils.explainStdin();
Expand All @@ -62,6 +66,7 @@ export const command = new Command("database:update <path> [infile]")
method: "PATCH",
path: jsonUrl.pathname,
body: inStream,
queryParams: jsonUrl.searchParams,
});
} catch (err: any) {
throw new FirebaseError("Unexpected error while setting data");
Expand Down
5 changes: 3 additions & 2 deletions src/database/remove.ts
Expand Up @@ -29,10 +29,11 @@ export default class DatabaseRemove {
* @param instance RTBD instance ID.
* @param path path to delete.
* @param host db host.
* @param disableTriggers if true, suppresses any Cloud functions that would be triggered by this operation.
*/
constructor(instance: string, path: string, host: string) {
constructor(instance: string, path: string, host: string, disableTriggers: boolean) {
this.path = path;
this.remote = new RTDBRemoveRemote(instance, host);
this.remote = new RTDBRemoveRemote(instance, host, disableTriggers);
this.deleteJobStack = new Stack({
name: "delete stack",
concurrency: 1,
Expand Down
10 changes: 8 additions & 2 deletions src/database/removeRemote.ts
Expand Up @@ -22,10 +22,12 @@ export class RTDBRemoveRemote implements RemoveRemote {
private instance: string;
private host: string;
private apiClient: Client;
private disableTriggers: boolean;

constructor(instance: string, host: string) {
constructor(instance: string, host: string, disableTriggers: boolean) {
this.instance = instance;
this.host = host;
this.disableTriggers = disableTriggers;

const url = new URL(utils.getDatabaseUrl(this.host, this.instance, "/"));
this.apiClient = new Client({ urlPrefix: url.origin, auth: true });
Expand All @@ -46,7 +48,11 @@ export class RTDBRemoveRemote implements RemoveRemote {
private async patch(path: string, body: any, note: string): Promise<boolean> {
const t0 = Date.now();
const url = new URL(utils.getDatabaseUrl(this.host, this.instance, path + ".json"));
const queryParams = { print: "silent", writeSizeLimit: "tiny" };
const queryParams = {
print: "silent",
writeSizeLimit: "tiny",
disableTriggers: this.disableTriggers.toString(),
};
const res = await this.apiClient.request({
method: "PATCH",
path: url.pathname,
Expand Down
18 changes: 14 additions & 4 deletions src/test/database/remove.spec.ts
Expand Up @@ -8,7 +8,7 @@ const HOST = "https://firebaseio.com";
describe("DatabaseRemove", () => {
it("should remove tiny tree", async () => {
const fakeDb = new FakeRemoveRemote({ c: 1 });
const removeOps = new DatabaseRemove("test-tiny-tree", "/", HOST);
const removeOps = new DatabaseRemove("test-tiny-tree", "/", HOST, /* disableTriggers= */ false);
removeOps.remote = fakeDb;
await removeOps.execute();
expect(fakeDb.data).to.eql(null);
Expand All @@ -29,7 +29,7 @@ describe("DatabaseRemove", () => {
const fakeList = new FakeListRemote(data);
const fakeDb = new FakeRemoveRemote(data);

const removeOps = new DatabaseRemove("test-sub-path", "/a", HOST);
const removeOps = new DatabaseRemove("test-sub-path", "/a", HOST, /* disableTriggers= */ false);
removeOps.remote = fakeDb;
removeOps.listRemote = fakeList;
await removeOps.execute();
Expand Down Expand Up @@ -57,7 +57,12 @@ describe("DatabaseRemove", () => {
const data = buildData(3, 5);
const fakeDb = new FakeRemoveRemote(data, threshold);
const fakeLister = new FakeListRemote(data);
const removeOps = new DatabaseRemove("test-nested-tree", "/", HOST);
const removeOps = new DatabaseRemove(
"test-nested-tree",
"/",
HOST,
/* disableTriggers= */ false
);
removeOps.remote = fakeDb;
removeOps.listRemote = fakeLister;
await removeOps.execute();
Expand All @@ -68,7 +73,12 @@ describe("DatabaseRemove", () => {
const data = buildData(1232, 1);
const fakeDb = new FakeRemoveRemote(data, threshold);
const fakeList = new FakeListRemote(data);
const removeOps = new DatabaseRemove("test-remover", "/", HOST);
const removeOps = new DatabaseRemove(
"test-remover",
"/",
HOST,
/* disableTriggers= */ false
);
removeOps.remote = fakeDb;
removeOps.listRemote = fakeList;
await removeOps.execute();
Expand Down
23 changes: 18 additions & 5 deletions src/test/database/removeRemote.spec.ts
Expand Up @@ -7,7 +7,7 @@ import { RTDBRemoveRemote } from "../../database/removeRemote";
describe("RemoveRemote", () => {
const instance = "fake-db";
const host = "https://firebaseio.com";
const remote = new RTDBRemoveRemote(instance, host);
const remote = new RTDBRemoveRemote(instance, host, /* disableTriggers= */ false);
const serverUrl = utils.getDatabaseUrl(host, instance, "");

afterEach(() => {
Expand All @@ -17,15 +17,15 @@ describe("RemoveRemote", () => {
it("should return true when patch is small", () => {
nock(serverUrl)
.patch("/a/b.json")
.query({ print: "silent", writeSizeLimit: "tiny" })
.query({ print: "silent", writeSizeLimit: "tiny", disableTriggers: "false" })
.reply(200, {});
return expect(remote.deletePath("/a/b")).to.eventually.eql(true);
});

it("should return false whem patch is large", () => {
nock(serverUrl)
.patch("/a/b.json")
.query({ print: "silent", writeSizeLimit: "tiny" })
.query({ print: "silent", writeSizeLimit: "tiny", disableTriggers: "false" })
.reply(400, {
error:
"Data requested exceeds the maximum size that can be accessed with a single request.",
Expand All @@ -36,19 +36,32 @@ describe("RemoveRemote", () => {
it("should return true when multi-path patch is small", () => {
nock(serverUrl)
.patch("/a/b.json")
.query({ print: "silent", writeSizeLimit: "tiny" })
.query({ print: "silent", writeSizeLimit: "tiny", disableTriggers: "false" })
.reply(200, {});
return expect(remote.deleteSubPath("/a/b", ["1", "2", "3"])).to.eventually.eql(true);
});

it("should return false when multi-path patch is large", () => {
nock(serverUrl)
.patch("/a/b.json")
.query({ print: "silent", writeSizeLimit: "tiny" })
.query({ print: "silent", writeSizeLimit: "tiny", disableTriggers: "false" })
tohhsinpei marked this conversation as resolved.
Show resolved Hide resolved
.reply(400, {
error:
"Data requested exceeds the maximum size that can be accessed with a single request.",
});
return expect(remote.deleteSubPath("/a/b", ["1", "2", "3"])).to.eventually.eql(false);
});

it("should send disableTriggers param", () => {
const remoteWithDisableTriggers = new RTDBRemoveRemote(
instance,
host,
/* disableTriggers= */ true
);
nock(serverUrl)
.patch("/a/b.json")
.query({ print: "silent", writeSizeLimit: "tiny", disableTriggers: "true" })
.reply(200, {});
return expect(remoteWithDisableTriggers.deletePath("/a/b")).to.eventually.eql(true);
});
});