Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/deploy/firestore/prepare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe("firestore prepare", () => {
let sandbox: sinon.SinonSandbox;
let getDatabaseStub: sinon.SinonStub;
let createDatabaseStub: sinon.SinonStub;
let getFirestoreConfigStub: sinon.SinonStub;

beforeEach(() => {
sandbox = sinon.createSandbox();
Expand All @@ -23,7 +24,7 @@ describe("firestore prepare", () => {
sandbox.stub(loadCJSON, "loadCJSON").returns({});
sandbox.stub(RulesDeploy.prototype, "addFile").returns();
sandbox.stub(RulesDeploy.prototype, "compile").resolves();
sandbox.stub(fsConfig, "getFirestoreConfig").returns([
getFirestoreConfigStub = sandbox.stub(fsConfig, "getFirestoreConfig").returns([
{
database: "test-db",
rules: "firestore.rules",
Expand All @@ -36,6 +37,47 @@ describe("firestore prepare", () => {
sandbox.restore();
});

describe("database-scoped resource selection", () => {
const options = {
projectId: "test-project",
only: "firestore:test-db:rules",
config: {
path: (p: string) => p,
data: {
firestore: {
database: "test-db",
},
},
},
} as unknown as Options;

it("should prepare only rules for a database-scoped rules target", async () => {
getFirestoreConfigStub.returns([{ database: "test-db", rules: "firestore.rules" }]);
const context: Record<string, unknown> = { projectId: "test-project" };

await prepare(context, options);

expect(context["firestoreRules"]).to.be.true;
expect(context["firestoreIndexes"]).to.be.false;
const firestore = context["firestore"] as { rules: unknown[]; indexes: unknown[] };
expect(firestore.rules).to.have.length(1);
expect(firestore.indexes).to.have.length(0);
});

it("should prepare only indexes for a database-scoped indexes target", async () => {
getFirestoreConfigStub.returns([{ database: "test-db", indexes: "firestore.indexes.json" }]);
const context: Record<string, unknown> = { projectId: "test-project" };

await prepare(context, { ...options, only: "firestore:test-db:indexes" });

expect(context["firestoreRules"]).to.be.false;
expect(context["firestoreIndexes"]).to.be.true;
const firestore = context["firestore"] as { rules: unknown[]; indexes: unknown[] };
expect(firestore.rules).to.have.length(0);
expect(firestore.indexes).to.have.length(1);
});
});

describe("createDatabase", () => {
const projectId = "test-project";
const options = {
Expand Down
24 changes: 2 additions & 22 deletions src/deploy/firestore/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,32 +151,12 @@ async function createDatabase(context: any, options: Options): Promise<void> {
export default async function (context: any, options: DeployOptions): Promise<void> {
await ensure(context.projectId, firestoreOrigin(), "firestore");
await ensure(context.projectId, firestoreOrigin(), "firestore");
if (options.only) {
const targets = options.only.split(",");

// Used for edge case when deploying to a named database
// https://github.com/firebase/firebase-tools/pull/6129
const excludeRules = targets.includes("firestore:indexes");
const excludeIndexes = targets.includes("firestore:rules");

// Used for edge case when deploying --only firestore:rules,firestore:indexes
// https://github.com/firebase/firebase-tools/issues/6857
const includeRules = targets.includes("firestore:rules");
const includeIndexes = targets.includes("firestore:indexes");

const onlyFirestore = targets.includes("firestore");

context.firestoreIndexes = !excludeIndexes || includeIndexes || onlyFirestore;
context.firestoreRules = !excludeRules || includeRules || onlyFirestore;
} else {
context.firestoreIndexes = true;
context.firestoreRules = true;
}

const firestoreConfigs: fsConfig.ParsedFirestoreConfig[] = fsConfig.getFirestoreConfig(
context.projectId,
options,
);
context.firestoreIndexes = firestoreConfigs.some((config) => Boolean(config.indexes));
context.firestoreRules = firestoreConfigs.some((config) => Boolean(config.rules));
if (!firestoreConfigs || firestoreConfigs.length === 0) {
return;
}
Expand Down
119 changes: 82 additions & 37 deletions src/firestore/fsConfig.spec.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,95 @@
import { expect } from "chai";
import { FirebaseError } from "../error";
import { Options } from "../options";
import { getFirestoreConfig } from "./fsConfig";

describe("getFirestoreConfig", () => {
it("should return all configs when firestore:indexes is specified in only", () => {
const options: any = {
config: {
src: {
firestore: [
{ database: "(default)", rules: "firestore.rules" },
{ database: "second", rules: "firestore.second.rules" },
],
},
},
rc: {
requireTarget: () => {
return;
},
target: () => [],
function makeOptions(only: string): Options {
return {
config: {
src: {
firestore: [
{ database: "(default)", rules: "firestore.rules", indexes: "firestore.indexes.json" },
{
database: "second",
rules: "firestore.second.rules",
indexes: "firestore.second.indexes.json",
},
],
},
only: "firestore:indexes",
};
},
rc: {
requireTarget: () => undefined,
target: () => [],
},
only,
} as unknown as Options;
}

const result = getFirestoreConfig("project", options);
describe("getFirestoreConfig", () => {
it("should return all configs with only indexes when firestore:indexes is specified", () => {
const result = getFirestoreConfig("project", makeOptions("firestore:indexes"));
expect(result).to.have.length(2);
expect(result.map((config) => config.indexes)).to.deep.equal([
"firestore.indexes.json",
"firestore.second.indexes.json",
]);
expect(result.every((config) => config.rules === undefined)).to.be.true;
});

it("should return all configs when firestore:rules is specified in only", () => {
const options: any = {
config: {
src: {
firestore: [
{ database: "(default)", rules: "firestore.rules" },
{ database: "second", rules: "firestore.second.rules" },
],
},
it("should return all configs with only rules when firestore:rules is specified", () => {
const result = getFirestoreConfig("project", makeOptions("firestore:rules"));
expect(result).to.have.length(2);
expect(result.map((config) => config.rules)).to.deep.equal([
"firestore.rules",
"firestore.second.rules",
]);
expect(result.every((config) => config.indexes === undefined)).to.be.true;
});

it("should return both resources for a database-scoped target", () => {
const result = getFirestoreConfig("project", makeOptions("firestore:second"));
expect(result).to.deep.equal([
{
database: "second",
rules: "firestore.second.rules",
indexes: "firestore.second.indexes.json",
},
rc: {
requireTarget: () => {
return;
},
target: () => [],
]);
});

it("should select only rules for a database-scoped target", () => {
const result = getFirestoreConfig("project", makeOptions("firestore:second:rules"));
expect(result).to.deep.equal([
{ database: "second", rules: "firestore.second.rules", indexes: undefined },
]);
});

it("should select only indexes for a database-scoped target", () => {
const result = getFirestoreConfig("project", makeOptions("firestore:second:indexes"));
expect(result).to.deep.equal([
{ database: "second", rules: undefined, indexes: "firestore.second.indexes.json" },
]);
});

it("should combine global and database-scoped resource selections", () => {
const result = getFirestoreConfig(
"project",
makeOptions("firestore:rules,firestore:second:indexes"),
);
expect(result).to.deep.equal([
{ database: "(default)", rules: "firestore.rules", indexes: undefined },
{
database: "second",
rules: "firestore.second.rules",
indexes: "firestore.second.indexes.json",
},
only: "firestore:rules",
};
]);
});

const result = getFirestoreConfig("project", options);
expect(result).to.have.length(2);
it("should reject unsupported database-scoped resources", () => {
expect(() => getFirestoreConfig("project", makeOptions("firestore:second:unknown"))).to.throw(
FirebaseError,
"Invalid Firestore deploy target: firestore:second:unknown",
);
});
});
Loading
Loading