Skip to content

Commit

Permalink
RTDB Rules Reload (#1520)
Browse files Browse the repository at this point in the history
  • Loading branch information
samtstern authored Jul 17, 2019
1 parent d6e0e80 commit a499da5
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 15 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Allow the RTDB emulator to hot-reload rules file on changes.
41 changes: 28 additions & 13 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EmulatorRegistry } from "../emulator/registry";
import { ALL_EMULATORS, EmulatorInstance, Emulators } from "../emulator/types";
import { Constants } from "../emulator/constants";
import { FunctionsEmulator } from "../emulator/functionsEmulator";
import { DatabaseEmulator } from "../emulator/databaseEmulator";
import { DatabaseEmulator, DatabaseEmulatorArgs } from "../emulator/databaseEmulator";
import { FirestoreEmulator, FirestoreEmulatorArgs } from "../emulator/firestoreEmulator";
import { HostingEmulator } from "../emulator/hostingEmulator";
import * as FirebaseError from "../error";
Expand Down Expand Up @@ -166,22 +166,37 @@ export async function startAll(options: any): Promise<void> {

if (shouldStart(options, Emulators.DATABASE)) {
const databaseAddr = Constants.getAddress(Emulators.DATABASE, options);
let databaseEmulator;

const args: DatabaseEmulatorArgs = {
host: databaseAddr.host,
port: databaseAddr.port,
projectId,
auto_download: true,
};

if (shouldStart(options, Emulators.FUNCTIONS)) {
const functionsAddr = Constants.getAddress(Emulators.FUNCTIONS, options);
databaseEmulator = new DatabaseEmulator({
host: databaseAddr.host,
port: databaseAddr.port,
functions_emulator_host: functionsAddr.host,
functions_emulator_port: functionsAddr.port,
auto_download: true,
});
args.functions_emulator_host = functionsAddr.host;
args.functions_emulator_port = functionsAddr.port;
}

const rulesLocalPath = options.config.get("database.rules");
if (rulesLocalPath) {
const rules: string = path.join(options.projectRoot, rulesLocalPath);
if (fs.existsSync(rules)) {
args.rules = rules;
} else {
utils.logWarning(
`Database rules file ${clc.bold(
rules
)} specified in firebase.json does not exist, starting Database emulator without rules.`
);
}
} else {
databaseEmulator = new DatabaseEmulator({
host: databaseAddr.host,
port: databaseAddr.port,
});
utils.logWarning(`No Database rules file specified in firebase.json, using default rules.`);
}

const databaseEmulator = new DatabaseEmulator(args);
await startEmulator(databaseEmulator);
}

Expand Down
57 changes: 56 additions & 1 deletion src/emulator/databaseEmulator.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
import * as chokidar from "chokidar";
import * as clc from "cli-color";
import * as fs from "fs";
import * as path from "path";
import * as request from "request";

import * as utils from "../utils";
import * as javaEmulators from "../serve/javaEmulators";
import { EmulatorInfo, EmulatorInstance, Emulators } from "../emulator/types";
import { Constants } from "./constants";

interface DatabaseEmulatorArgs {
export interface DatabaseEmulatorArgs {
port?: number;
host?: string;
projectId?: string;
rules?: string;
functions_emulator_port?: number;
functions_emulator_host?: string;
auto_download?: boolean;
}

export class DatabaseEmulator implements EmulatorInstance {
rulesWatcher?: chokidar.FSWatcher;

constructor(private args: DatabaseEmulatorArgs) {}

async start(): Promise<void> {
if (this.args.rules && this.args.projectId) {
const rulesPath = this.args.rules;
this.rulesWatcher = chokidar.watch(rulesPath, { persistent: true, ignoreInitial: true });
this.rulesWatcher.on("change", async (event, stats) => {
const newContent = fs.readFileSync(rulesPath).toString();

utils.logLabeledBullet("database", "Change detected, updating rules...");
try {
await this.updateRules(newContent);
utils.logLabeledSuccess("database", "Rules updated.");
} catch (e) {
utils.logWarning(this.prettyPrintRulesError(rulesPath, e));
utils.logWarning("Failed to update rules");
}
});
}

return javaEmulators.start(Emulators.DATABASE, this.args);
}

Expand All @@ -39,4 +67,31 @@ export class DatabaseEmulator implements EmulatorInstance {
getName(): Emulators {
return Emulators.DATABASE;
}

private updateRules(content: string): Promise<any> {
const { host, port } = this.getInfo();
return new Promise((resolve, reject) => {
request.put(
{
uri: `http://${host}:${[port]}/.settings/rules.json?ns=${this.args.projectId}`,
headers: { Authorization: "Bearer owner" },
body: content,
},
(err, resp, body) => {
if (err) {
reject(err);
} else if (resp.statusCode !== 200) {
reject(JSON.parse(body).error);
} else {
resolve();
}
}
);
});
}

private prettyPrintRulesError(filePath: string, error: string): string {
const relativePath = path.relative(process.cwd(), filePath);
return `${clc.cyan(relativePath)}:${error.trim()}`;
}
}
2 changes: 1 addition & 1 deletion src/serve/javaEmulators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const Commands: { [s in JavaEmulators]: JavaEmulatorCommand } = {
database: {
binary: "java",
args: ["-Duser.language=en", "-jar", EmulatorDetails.database.localPath],
optionalArgs: ["port", "host", "rules", "functions_emulator_port", "functions_emulator_host"],
optionalArgs: ["port", "host", "functions_emulator_port", "functions_emulator_host"],
},
firestore: {
binary: "java",
Expand Down

0 comments on commit a499da5

Please sign in to comment.