-
Notifications
You must be signed in to change notification settings - Fork 917
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Typescriptify project init flow (#1370)
* convert firebaseApi and init/features/project to typescript * change to use async/await * add jsdoc on the interfaces * fix default export * format * remove default function, ts-ify storage * add cast to Question to keep validate function * delete project.d.ts and move interfaces in with source code * fix interfaces, remote lint disables, refactor * Error => FirebaseError * review comment fixes, remove validate function
- Loading branch information
Showing
6 changed files
with
204 additions
and
175 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import * as api from "./api"; | ||
|
||
const API_VERSION = "v1beta1"; | ||
|
||
/** | ||
* Represents the FirebaseProject resource returned from calling | ||
* `projects.get` in Firebase Management API: | ||
* https://firebase.google.com/docs/projects/api/reference/rest/v1beta1/projects#FirebaseProject | ||
*/ | ||
export interface FirebaseProject { | ||
projectId: string; | ||
projectNumber: number; | ||
displayName: string; | ||
name: string; | ||
resources: { | ||
hostingSite?: string; | ||
realtimeDatabaseInstance?: string; | ||
storageBucket?: string; | ||
locationId?: string; | ||
}; | ||
} | ||
|
||
export async function listProjects( | ||
nextPageToken?: string, | ||
projectsList: FirebaseProject[] = [] | ||
): Promise<FirebaseProject[]> { | ||
let path = `/${API_VERSION}/projects?page_size=100`; | ||
if (nextPageToken) { | ||
path += `&page_token=${nextPageToken}`; | ||
} | ||
|
||
const response = await api.request("GET", path, { | ||
auth: true, | ||
origin: api.firebaseApiOrigin, | ||
}); | ||
projectsList = projectsList.concat(response.body.results); | ||
if (response.body.nextPageToken) { | ||
return listProjects(response.body.nextPageToken, projectsList); | ||
} | ||
return projectsList; | ||
} | ||
|
||
export async function getProject(projectId: string): Promise<FirebaseProject> { | ||
const response = await api.request("GET", `/${API_VERSION}/projects/${projectId}`, { | ||
auth: true, | ||
origin: api.firebaseApiOrigin, | ||
}); | ||
return response.body; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import * as clc from "cli-color"; | ||
import * as _ from "lodash"; | ||
|
||
import * as Config from "../../config"; | ||
import * as FirebaseError from "../../error"; | ||
import { FirebaseProject, getProject, listProjects } from "../../firebaseApi"; | ||
import * as logger from "../../logger"; | ||
import { promptOnce, Question } from "../../prompt"; | ||
import * as utils from "../../utils"; | ||
|
||
const NO_PROJECT = "[don't setup a default project]"; | ||
const NEW_PROJECT = "[create a new project]"; | ||
|
||
/** | ||
* Used in init flows to keep information about the project - basically | ||
* a shorter version of {@link FirebaseProject} with some additional fields. | ||
*/ | ||
export interface ProjectInfo { | ||
id: string; // maps to FirebaseProject.projectId | ||
label?: string; | ||
instance?: string; // maps to FirebaseProject.resources.realtimeDatabaseInstance | ||
location?: string; // maps to FirebaseProject.resources.locationId | ||
} | ||
|
||
/** | ||
* Get the user's desired project, prompting if necessary. | ||
* @returns A {@link ProjectInfo} object. | ||
*/ | ||
async function getProjectInfo(options: any): Promise<ProjectInfo> { | ||
if (options.project) { | ||
return selectProjectFromOptions(options); | ||
} | ||
return selectProjectFromList(options); | ||
} | ||
|
||
/** | ||
* Selects project when --project is passed in. | ||
* @param options Command line options. | ||
* @returns A {@link FirebaseProject} object. | ||
*/ | ||
async function selectProjectFromOptions(options: any): Promise<ProjectInfo> { | ||
let project: FirebaseProject; | ||
try { | ||
project = await getProject(options.project); | ||
} catch (e) { | ||
throw new FirebaseError(`Error getting project ${options.project}: ${e}`); | ||
} | ||
const projectId = project.projectId; | ||
const name = project.displayName; | ||
return { | ||
id: projectId, | ||
label: `${projectId} (${name})`, | ||
instance: _.get(project, "resources.realtimeDatabaseInstance"), | ||
}; | ||
} | ||
|
||
/** | ||
* Presents user with list of projects to choose from and gets project | ||
* information for chosen project. | ||
* @param options Command line options. | ||
* @returns A {@link FirebaseProject} object. | ||
*/ | ||
async function selectProjectFromList(options: any): Promise<ProjectInfo> { | ||
const projects: FirebaseProject[] = await listProjects(); | ||
let choices = projects.filter((p: FirebaseProject) => !!p).map((p) => { | ||
return { | ||
name: `${p.projectId} (${p.displayName})`, | ||
value: p.projectId, | ||
}; | ||
}); | ||
choices = _.orderBy(choices, ["name"], ["asc"]); | ||
choices.unshift({ name: NO_PROJECT, value: NO_PROJECT }); | ||
choices.push({ name: NEW_PROJECT, value: NEW_PROJECT }); | ||
|
||
if (choices.length >= 25) { | ||
utils.logBullet( | ||
`Don't want to scroll through all your projects? If you know your project ID, ` + | ||
`you can initialize it directly using ${clc.bold( | ||
"firebase init --project <project_id>" | ||
)}.\n` | ||
); | ||
} | ||
const projectId: string = await promptOnce({ | ||
type: "list", | ||
name: "id", | ||
message: "Select a default Firebase project for this directory:", | ||
choices, | ||
}); | ||
if (projectId === NEW_PROJECT || projectId === NO_PROJECT) { | ||
return { id: projectId }; | ||
} | ||
|
||
let project: FirebaseProject | undefined; | ||
project = projects.find((p) => p.projectId === projectId); | ||
const pId = choices.find((p) => p.value === projectId); | ||
const label = pId ? pId.name : ""; | ||
|
||
return { | ||
id: projectId, | ||
label, | ||
instance: _.get(project, "resources.realtimeDatabaseInstance"), | ||
}; | ||
} | ||
|
||
/** | ||
* Sets up the default project if provided and writes .firebaserc file. | ||
* @param setup A helper object to use for the rest of the init features. | ||
* @param config Configuration for the project. | ||
* @param options Command line options. | ||
*/ | ||
export async function doSetup(setup: any, config: Config, options: any): Promise<void> { | ||
setup.project = {}; | ||
|
||
logger.info(); | ||
logger.info(`First, let's associate this project directory with a Firebase project.`); | ||
logger.info( | ||
`You can create multiple project aliases by running ${clc.bold("firebase use --add")}, ` | ||
); | ||
logger.info(`but for now we'll just set up a default project.`); | ||
logger.info(); | ||
|
||
if (_.has(setup.rcfile, "projects.default")) { | ||
utils.logBullet(`.firebaserc already has a default project, skipping`); | ||
setup.projectId = _.get(setup.rcfile, "projects.default"); | ||
return; | ||
} | ||
|
||
const projectInfo = await getProjectInfo(options); | ||
if (projectInfo.id === NEW_PROJECT) { | ||
setup.createProject = true; | ||
return; | ||
} else if (projectInfo.id === NO_PROJECT) { | ||
return; | ||
} | ||
|
||
utils.logBullet(`Using project ${projectInfo.label}`); | ||
|
||
// write "default" alias and activate it immediately | ||
_.set(setup.rcfile, "projects.default", projectInfo.id); | ||
setup.projectId = projectInfo.id; | ||
setup.instance = projectInfo.instance; | ||
utils.makeActiveProject(config.projectDir, projectInfo.id); | ||
} |
Oops, something went wrong.