Skip to content

Commit

Permalink
Merge 15ad1c7 into 2dc7216
Browse files Browse the repository at this point in the history
  • Loading branch information
bkendall committed Jan 9, 2021
2 parents 2dc7216 + 15ad1c7 commit 3fe8ac5
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/commands/database-instances-list.ts
Expand Up @@ -5,7 +5,7 @@ import * as ora from "ora";

import logger = require("../logger");
import { requirePermissions } from "../requirePermissions";
import getProjectNumber = require("../getProjectNumber");
import { getProjectNumber } from "../getProjectNumber";
import firedata = require("../gcp/firedata");
import { Emulators } from "../emulator/types";
import { warnEmulatorNotSupported } from "../emulator/commandUtils";
Expand Down
2 changes: 1 addition & 1 deletion src/commands/serve.js
Expand Up @@ -10,7 +10,7 @@ var { requirePermissions } = require("../requirePermissions");
var requireConfig = require("../requireConfig");
var { serve } = require("../serve/index");
var filterTargets = require("../filterTargets");
var getProjectNumber = require("../getProjectNumber");
var { getProjectNumber } = require("../getProjectNumber");
var { FirebaseError } = require("../error");

var VALID_TARGETS = ["hosting", "functions"];
Expand Down
2 changes: 1 addition & 1 deletion src/deploy/remoteconfig/prepare.ts
@@ -1,4 +1,4 @@
import getProjectNumber = require("../../getProjectNumber");
import { getProjectNumber } from "../../getProjectNumber";
import loadCJSON = require("../../loadCJSON");
import { getEtag } from "./functions";
import { validateInputRemoteConfigTemplate } from "./functions";
Expand Down
2 changes: 1 addition & 1 deletion src/deploy/remoteconfig/release.ts
@@ -1,5 +1,5 @@
import { publishTemplate, getEtag } from "./functions";
import getProjectNumber = require("../../getProjectNumber");
import { getProjectNumber } from "../../getProjectNumber";
import { RemoteConfigTemplate } from "../../remoteconfig/interfaces";

interface ReleaseContext {
Expand Down
20 changes: 12 additions & 8 deletions src/fetchWebSetup.ts
@@ -1,6 +1,7 @@
import * as api from "./api";
import * as getProjectId from "./getProjectId";
import { Client } from "./apiv2";
import { configstore } from "./configstore";
import { firebaseApiOrigin } from "./api";
import * as getProjectId from "./getProjectId";

export interface WebConfig {
projectId: string;
Expand All @@ -13,16 +14,20 @@ export interface WebConfig {
messagingSenderId?: string;
}

const apiClient = new Client({ urlPrefix: firebaseApiOrigin, auth: true, apiVersion: "v1beta1" });

const CONFIGSTORE_KEY = "webconfig";

function setCachedWebSetup(projectId: string, config: WebConfig) {
function setCachedWebSetup(projectId: string, config: WebConfig): void {
const allConfigs = configstore.get(CONFIGSTORE_KEY) || {};
allConfigs[projectId] = config;
configstore.set(CONFIGSTORE_KEY, allConfigs);
}

/**
* Get the last known WebConfig from the cache.
* @param options CLI options.
* @return web app configuration, or undefined.
*/
export function getCachedWebSetup(options: any): WebConfig | undefined {
const projectId = getProjectId(options, false);
Expand All @@ -32,14 +37,13 @@ export function getCachedWebSetup(options: any): WebConfig | undefined {

/**
* TODO: deprecate this function in favor of `getAppConfig()` in `/src/management/apps.ts`
* @param options CLI options.
* @return web app configuration.
*/
export async function fetchWebSetup(options: any): Promise<WebConfig> {
const projectId = getProjectId(options, false);
const response = await api.request("GET", `/v1beta1/projects/${projectId}/webApps/-/config`, {
auth: true,
origin: api.firebaseApiOrigin,
});
const config = response.body;
const res = await apiClient.get<WebConfig>(`/projects/${projectId}/webApps/-/config`);
const config = res.body;
setCachedWebSetup(config.projectId, config);
return config;
}
2 changes: 1 addition & 1 deletion src/gcp/firedata.ts
Expand Up @@ -46,7 +46,7 @@ export async function createDatabaseInstance(
* @param projectId Project from which you want to get the ruleset.
* @param instanceName The name for the new Realtime Database instance.
*/
export async function listDatabaseInstances(projectNumber: number): Promise<DatabaseInstance[]> {
export async function listDatabaseInstances(projectNumber: string): Promise<DatabaseInstance[]> {
const response = await api.request("GET", `/v1/projects/${projectNumber}/databases`, {
auth: true,
origin: api.firedataOrigin,
Expand Down
20 changes: 0 additions & 20 deletions src/getProjectNumber.js

This file was deleted.

17 changes: 17 additions & 0 deletions src/getProjectNumber.ts
@@ -0,0 +1,17 @@
import { getFirebaseProject } from "./management/projects";
import * as getProjectId from "./getProjectId";

/**
* Fetches the project number.
* @param options CLI options.
* @return the project number, as a string.
*/
export async function getProjectNumber(options: any): Promise<string> {
if (options.projectNumber) {
return options.projectNumber;
}
const projectId = getProjectId(options);
const metadata = await getFirebaseProject(projectId);
options.projectNumber = metadata.projectNumber;
return options.projectNumber;
}
97 changes: 97 additions & 0 deletions src/test/fetchWebSetup.spec.ts
@@ -0,0 +1,97 @@
import { expect } from "chai";
import * as nock from "nock";
import * as sinon from "sinon";

import { configstore } from "../configstore";
import { fetchWebSetup, getCachedWebSetup } from "../fetchWebSetup";
import { firebaseApiOrigin } from "../api";
import { FirebaseError } from "../error";

describe("fetchWebSetup module", () => {
afterEach(() => {
expect(nock.isDone()).to.be.true;
});

describe("fetchWebSetup", () => {
let configSetStub: sinon.SinonStub;

beforeEach(() => {
sinon.stub(configstore, "get");
configSetStub = sinon.stub(configstore, "set").returns();
});

afterEach(() => {
sinon.restore();
});

it("should fetch the web app config", async () => {
const projectId = "foo";
nock(firebaseApiOrigin)
.get(`/v1beta1/projects/${projectId}/webApps/-/config`)
.reply(200, { some: "config" });

const config = await fetchWebSetup({ project: projectId });

expect(config).to.deep.equal({ some: "config" });
});

it("should store the fetched config", async () => {
const projectId = "projectId";
nock(firebaseApiOrigin)
.get(`/v1beta1/projects/${projectId}/webApps/-/config`)
.reply(200, { projectId, some: "config" });

await fetchWebSetup({ project: projectId });

expect(configSetStub).to.have.been.calledOnceWith("webconfig", {
[projectId]: {
projectId,
some: "config",
},
});
expect(nock.isDone()).to.be.true;
});

it("should throw an error if the request fails", async () => {
const projectId = "foo";
nock(firebaseApiOrigin)
.get(`/v1beta1/projects/${projectId}/webApps/-/config`)
.reply(404, { error: "Not Found" });

await expect(fetchWebSetup({ project: projectId })).to.eventually.be.rejectedWith(
FirebaseError,
"Not Found"
);
});
});

describe("getCachedWebSetup", () => {
let configGetStub: sinon.SinonStub;

beforeEach(() => {
sinon.stub(configstore, "set").returns();
configGetStub = sinon.stub(configstore, "get");
});

afterEach(() => {
sinon.restore();
});

it("should return no config if none is cached", () => {
configGetStub.returns(undefined);

const config = getCachedWebSetup({ project: "foo" });

expect(config).to.be.undefined;
});

it("should return a stored config", () => {
const projectId = "projectId";
configGetStub.returns({ [projectId]: { project: projectId, some: "config" } });

const config = getCachedWebSetup({ project: projectId });

expect(config).to.be.deep.equal({ project: projectId, some: "config" });
});
});
});
42 changes: 42 additions & 0 deletions src/test/getProjectNumber.spec.ts
@@ -0,0 +1,42 @@
import { expect } from "chai";
import * as sinon from "sinon";

import { getProjectNumber } from "../getProjectNumber";
import * as projects from "../management/projects";

describe("getProjectNumber", () => {
let getProjectStub: sinon.SinonStub;

beforeEach(() => {
getProjectStub = sinon.stub(projects, "getFirebaseProject").throws(new Error("stubbed"));
});

afterEach(() => {
sinon.restore();
});

it("should return the project number from options, if present", async () => {
const n = await getProjectNumber({ projectNumber: 1 });

expect(n).to.equal(1);
expect(getProjectStub).to.not.have.been.called;
});

it("should fetch the project number if necessary", async () => {
getProjectStub.returns({ projectNumber: 2 });

const n = await getProjectNumber({ project: "foo" });

expect(n).to.equal(2);
expect(getProjectStub).to.have.been.calledOnceWithExactly("foo");
});

it("should reject with an error on an error", async () => {
getProjectStub.rejects(new Error("oh no"));

await expect(getProjectNumber({ project: "foo" })).to.eventually.be.rejectedWith(
Error,
"oh no"
);
});
});

0 comments on commit 3fe8ac5

Please sign in to comment.