Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/gcp/frameworks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ interface Codebase {
rootDirectory: string;
}

/** A Stack, the primary resource of Frameworks. */
export interface Stack {
/** A Backend, the primary resource of Frameworks. */
export interface Backend {
name: string;
mode?: string;
codebase: Codebase;
Expand All @@ -27,7 +27,7 @@ export interface Stack {
uri: string;
}

export type StackOutputOnlyFields = "name" | "createTime" | "updateTime" | "uri";
export type BackendOutputOnlyFields = "name" | "createTime" | "updateTime" | "uri";

export interface Build {
name: string;
Expand Down Expand Up @@ -82,21 +82,21 @@ export interface Operation {
}

export interface ListBackendsResponse {
backends: Stack[];
backends: Backend[];
}

/**
* Creates a new Stack in a given project and location.
* Creates a new Backend in a given project and location.
*/
export async function createStack(
export async function createBackend(
projectId: string,
location: string,
stackReqBoby: Omit<Stack, StackOutputOnlyFields>,
backendReqBoby: Omit<Backend, BackendOutputOnlyFields>,
backendId: string
): Promise<Operation> {
const res = await client.post<Omit<Stack, StackOutputOnlyFields>, Operation>(
const res = await client.post<Omit<Backend, BackendOutputOnlyFields>, Operation>(
`projects/${projectId}/locations/${location}/backends`,
stackReqBoby,
backendReqBoby,
{ queryParams: { backendId } }
);

Expand All @@ -110,9 +110,9 @@ export async function getBackend(
projectId: string,
location: string,
backendId: string
): Promise<Stack> {
): Promise<Backend> {
const name = `projects/${projectId}/locations/${location}/backends/${backendId}`;
const res = await client.get<Stack>(name);
const res = await client.get<Backend>(name);

return res.body;
}
Expand Down Expand Up @@ -150,12 +150,12 @@ export async function deleteBackend(
export async function createBuild(
projectId: string,
location: string,
stackId: string,
backendId: string,
buildInput: Omit<Build, BuildOutputOnlyFields>
): Promise<Operation> {
const buildId = buildInput.name;
const res = await client.post<Omit<Build, BuildOutputOnlyFields>, Operation>(
`projects/${projectId}/locations/${location}/backends/${stackId}/builds`,
`projects/${projectId}/locations/${location}/backends/${backendId}/builds`,
buildInput,
{ queryParams: { buildId } }
);
Expand Down
76 changes: 44 additions & 32 deletions src/init/features/frameworks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ALLOWED_DEPLOY_METHODS,
} from "./constants";
import * as repo from "./repo";
import { Stack, StackOutputOnlyFields } from "../../../gcp/frameworks";
import { Backend, BackendOutputOnlyFields } from "../../../gcp/frameworks";
import { Repository } from "../../../gcp/cloudbuild";
import * as poller from "../../../operation-poller";
import { frameworksOrigin } from "../../../api";
Expand Down Expand Up @@ -70,13 +70,13 @@ export async function doSetup(setup: any, projectId: string): Promise<void> {
setup.frameworks
);

const stack: Stack | undefined = await getOrCreateStack(projectId, setup);
if (stack) {
utils.logSuccess(`Successfully created a stack: ${stack.name}`);
const backend: Backend | undefined = await getOrCreateBackend(projectId, setup);
if (backend) {
utils.logSuccess(`Successfully created a backend: ${backend.name}`);
}
}

function toStack(cloudBuildConnRepo: Repository): Omit<Stack, StackOutputOnlyFields> {
function toBackend(cloudBuildConnRepo: Repository): Omit<Backend, BackendOutputOnlyFields> {
return {
codebase: {
repository: `${cloudBuildConnRepo.name}`,
Expand All @@ -87,48 +87,60 @@ function toStack(cloudBuildConnRepo: Repository): Omit<Stack, StackOutputOnlyFie
}

/**
* Creates stack if it doesn't exist.
* Creates backend if it doesn't exist.
*/
export async function getOrCreateStack(projectId: string, setup: any): Promise<Stack | undefined> {
export async function getOrCreateBackend(
projectId: string,
setup: any
): Promise<Backend | undefined> {
const location: string = setup.frameworks.region;
const deployMethod: string = setup.frameworks.deployMethod;
try {
return await getExistingStack(projectId, setup, location);
return await getExistingBackend(projectId, setup, location);
} catch (err: unknown) {
if ((err as FirebaseError).status === 404) {
logger.info("Creating new stack.");
logger.info("Creating new backend.");
if (deployMethod === "github") {
const cloudBuildConnRepo = await repo.linkGitHubRepository(projectId, location);
const stackDetails = toStack(cloudBuildConnRepo);
return await createStack(projectId, location, stackDetails, setup.frameworks.serviceName);
const backendDetails = toBackend(cloudBuildConnRepo);
return await createBackend(
projectId,
location,
backendDetails,
setup.frameworks.serviceName
);
}
} else {
throw new FirebaseError(
`Failed to get or create a stack using the given initialization details: ${err}`
`Failed to get or create a backend using the given initialization details: ${err}`
);
}
}

return undefined;
}

async function getExistingStack(projectId: string, setup: any, location: string): Promise<Stack> {
let stack = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
while (stack) {
async function getExistingBackend(
projectId: string,
setup: any,
location: string
): Promise<Backend> {
let backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
while (backend) {
setup.frameworks.serviceName = undefined;
await promptOnce(
{
name: "existingStack",
name: "existingBackend",
type: "confirm",
default: true,
message:
"A stack already exists for the given serviceName, do you want to use existing stack? (yes/no)",
"A backend already exists for the given serviceName, do you want to use existing backend? (yes/no)",
},
setup.frameworks
);
if (setup.frameworks.existingStack) {
logger.info("Using the existing stack.");
return stack;
if (setup.frameworks.existingBackend) {
logger.info("Using the existing backend.");
return backend;
}
await promptOnce(
{
Expand All @@ -139,28 +151,28 @@ async function getExistingStack(projectId: string, setup: any, location: string)
},
setup.frameworks
);
stack = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
setup.frameworks.existingStack = undefined;
backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
setup.frameworks.existingBackend = undefined;
}

return stack;
return backend;
}

/**
* Creates Stack object from long running operations.
* Creates backend object from long running operations.
*/
export async function createStack(
export async function createBackend(
projectId: string,
location: string,
stackReqBoby: Omit<Stack, StackOutputOnlyFields>,
stackId: string
): Promise<Stack> {
const op = await gcp.createStack(projectId, location, stackReqBoby, stackId);
const stack = await poller.pollOperation<Stack>({
backendReqBoby: Omit<Backend, BackendOutputOnlyFields>,
backendId: string
): Promise<Backend> {
const op = await gcp.createBackend(projectId, location, backendReqBoby, backendId);
const backend = await poller.pollOperation<Backend>({
...frameworksPollerOptions,
pollerName: `create-${projectId}-${location}-${stackId}`,
pollerName: `create-${projectId}-${location}-${backendId}`,
operationResourceName: op.name,
});

return stack;
return backend;
}
2 changes: 1 addition & 1 deletion src/init/features/frameworks/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function generateConnectionId(location: string): string {
}

/**
* Prompts the user to link their stack to a GitHub repository.
* Prompts the user to link their backend to a GitHub repository.
*/
export async function linkGitHubRepository(
projectId: string,
Expand Down
68 changes: 34 additions & 34 deletions src/test/init/frameworks/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { expect } from "chai";
import * as gcp from "../../../gcp/frameworks";
import * as repo from "../../../init/features/frameworks/repo";
import * as poller from "../../../operation-poller";
import { createStack, getOrCreateStack } from "../../../init/features/frameworks/index";
import { createBackend, getOrCreateBackend } from "../../../init/features/frameworks/index";
import { FirebaseError } from "../../../error";

describe("operationsConverter", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();

let pollOperationStub: sinon.SinonStub;
let createStackStub: sinon.SinonStub;
let getStackStub: sinon.SinonStub;
let createBackendStub: sinon.SinonStub;
let getBackendStub: sinon.SinonStub;
let linkGitHubRepositoryStub: sinon.SinonStub;

beforeEach(() => {
pollOperationStub = sandbox
.stub(poller, "pollOperation")
.throws("Unexpected pollOperation call");
createStackStub = sandbox.stub(gcp, "createStack").throws("Unexpected createStack call");
getStackStub = sandbox.stub(gcp, "getBackend").throws("Unexpected getBackend call");
createBackendStub = sandbox.stub(gcp, "createBackend").throws("Unexpected createBackend call");
getBackendStub = sandbox.stub(gcp, "getBackend").throws("Unexpected getBackend call");
linkGitHubRepositoryStub = sandbox
.stub(repo, "linkGitHubRepository")
.throws("Unexpected getBackend call");
Expand All @@ -30,16 +30,16 @@ describe("operationsConverter", () => {
sandbox.verifyAndRestore();
});

describe("createStack", () => {
describe("createBackend", () => {
const projectId = "projectId";
const location = "us-central1";
const stackId = "stackId";
const backendId = "backendId";
const op = {
name: `projects/${projectId}/locations/${location}/stacks/${stackId}`,
name: `projects/${projectId}/locations/${location}/backends/${backendId}`,
done: true,
};
const completeStack = {
name: `projects/${projectId}/locations/${location}/stacks/${stackId}`,
const completeBackend = {
name: `projects/${projectId}/locations/${location}/backends/${backendId}`,
labels: {},
createTime: "0",
updateTime: "1",
Expand All @@ -48,8 +48,8 @@ describe("operationsConverter", () => {
const setup = {
frameworks: {
region: location,
serviceName: stackId,
existingStack: true,
serviceName: backendId,
existingBackend: true,
deployMethod: "github",
},
};
Expand All @@ -59,46 +59,46 @@ describe("operationsConverter", () => {
createTime: "0",
updateTime: "1",
};
const stackInput = {
const backendInput = {
codebase: {
repository: cloudBuildConnRepo.name,
rootDirectory: "/",
},
labels: {},
};
it("should createStack", async () => {
createStackStub.resolves(op);
pollOperationStub.resolves(completeStack);
it("should createBackend", async () => {
createBackendStub.resolves(op);
pollOperationStub.resolves(completeBackend);

await createStack(projectId, location, stackInput, stackId);
await createBackend(projectId, location, backendInput, backendId);

expect(createStackStub).to.be.calledWith(projectId, location, stackInput);
expect(createBackendStub).to.be.calledWith(projectId, location, backendInput);
});

it("should return a stack, if user wants use the exiting stack", async () => {
getStackStub.resolves(completeStack);
it("should return a backend, if user wants use the exiting backend", async () => {
getBackendStub.resolves(completeBackend);

const result = await getOrCreateStack("projectId", setup);
const result = await getOrCreateBackend("projectId", setup);

expect(result).to.deep.equal(completeStack);
expect(getStackStub.calledOnceWithExactly(projectId, location, stackId)).to.be.true;
expect(result).to.deep.equal(completeBackend);
expect(getBackendStub.calledOnceWithExactly(projectId, location, backendId)).to.be.true;
});

it("should create a new stack, if stack doesn't exist", async () => {
const newStackId = "newStackId";
const newPath = `projects/${projectId}/locations/${location}/stacks/${newStackId}`;
setup.frameworks.serviceName = newStackId;
it("should create a new backend, if backend doesn't exist", async () => {
const newBackendId = "newBackendId";
const newPath = `projects/${projectId}/locations/${location}/backends/${newBackendId}`;
setup.frameworks.serviceName = newBackendId;
op.name = newPath;
completeStack.name = newPath;
getStackStub.throws(new FirebaseError("error", { status: 404 }));
completeBackend.name = newPath;
getBackendStub.throws(new FirebaseError("error", { status: 404 }));
linkGitHubRepositoryStub.resolves(cloudBuildConnRepo);
createStackStub.resolves(op);
pollOperationStub.resolves(completeStack);
createBackendStub.resolves(op);
pollOperationStub.resolves(completeBackend);

const result = await getOrCreateStack(projectId, setup);
const result = await getOrCreateBackend(projectId, setup);

expect(result).to.deep.equal(completeStack);
expect(createStackStub).to.be.calledWith(projectId, location, stackInput);
expect(result).to.deep.equal(completeBackend);
expect(createBackendStub).to.be.calledWith(projectId, location, backendInput);
});
});
});