Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
210 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,149 @@ | ||
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> { | ||
let project: FirebaseProject | undefined; | ||
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:", | ||
validate: (answer: any) => { | ||
if (!_.includes(choices, answer)) { | ||
return `Must specify a Firebase project to which you have access.`; | ||
} | ||
return true; | ||
}, | ||
choices, | ||
} as Question); | ||
if (projectId === NEW_PROJECT || projectId === NO_PROJECT) { | ||
return { id: projectId }; | ||
} | ||
|
||
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: 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.