No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

projects.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.PROJECTS_CREATE_QUESTIONS = exports.ProjectParentResourceType = void 0;
  4. const clc = require("colorette");
  5. const ora = require("ora");
  6. const apiv2_1 = require("../apiv2");
  7. const error_1 = require("../error");
  8. const operation_poller_1 = require("../operation-poller");
  9. const prompt_1 = require("../prompt");
  10. const api = require("../api");
  11. const logger_1 = require("../logger");
  12. const utils = require("../utils");
  13. const TIMEOUT_MILLIS = 30000;
  14. const MAXIMUM_PROMPT_LIST = 100;
  15. const PROJECT_LIST_PAGE_SIZE = 1000;
  16. const CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS = 15000;
  17. var ProjectParentResourceType;
  18. (function (ProjectParentResourceType) {
  19. ProjectParentResourceType["ORGANIZATION"] = "organization";
  20. ProjectParentResourceType["FOLDER"] = "folder";
  21. })(ProjectParentResourceType = exports.ProjectParentResourceType || (exports.ProjectParentResourceType = {}));
  22. exports.PROJECTS_CREATE_QUESTIONS = [
  23. {
  24. type: "input",
  25. name: "projectId",
  26. default: "",
  27. message: "Please specify a unique project id " +
  28. `(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
  29. },
  30. {
  31. type: "input",
  32. name: "displayName",
  33. default: "",
  34. message: "What would you like to call your project? (defaults to your project ID)",
  35. },
  36. ];
  37. const firebaseAPIClient = new apiv2_1.Client({
  38. urlPrefix: api.firebaseApiOrigin,
  39. auth: true,
  40. apiVersion: "v1beta1",
  41. });
  42. async function createFirebaseProjectAndLog(projectId, options) {
  43. const spinner = ora("Creating Google Cloud Platform project").start();
  44. try {
  45. await createCloudProject(projectId, options);
  46. spinner.succeed();
  47. }
  48. catch (err) {
  49. spinner.fail();
  50. throw err;
  51. }
  52. return addFirebaseToCloudProjectAndLog(projectId);
  53. }
  54. exports.createFirebaseProjectAndLog = createFirebaseProjectAndLog;
  55. async function addFirebaseToCloudProjectAndLog(projectId) {
  56. let projectInfo;
  57. const spinner = ora("Adding Firebase resources to Google Cloud Platform project").start();
  58. try {
  59. projectInfo = await addFirebaseToCloudProject(projectId);
  60. }
  61. catch (err) {
  62. spinner.fail();
  63. throw err;
  64. }
  65. spinner.succeed();
  66. logNewFirebaseProjectInfo(projectInfo);
  67. return projectInfo;
  68. }
  69. exports.addFirebaseToCloudProjectAndLog = addFirebaseToCloudProjectAndLog;
  70. function logNewFirebaseProjectInfo(projectInfo) {
  71. logger_1.logger.info("");
  72. if (process.platform === "win32") {
  73. logger_1.logger.info("=== Your Firebase project is ready! ===");
  74. }
  75. else {
  76. logger_1.logger.info("🎉🎉🎉 Your Firebase project is ready! 🎉🎉🎉");
  77. }
  78. logger_1.logger.info("");
  79. logger_1.logger.info("Project information:");
  80. logger_1.logger.info(` - Project ID: ${clc.bold(projectInfo.projectId)}`);
  81. logger_1.logger.info(` - Project Name: ${clc.bold(projectInfo.displayName)}`);
  82. logger_1.logger.info("");
  83. logger_1.logger.info("Firebase console is available at");
  84. logger_1.logger.info(`https://console.firebase.google.com/project/${clc.bold(projectInfo.projectId)}/overview`);
  85. }
  86. async function getOrPromptProject(options) {
  87. if (options.project) {
  88. return await getFirebaseProject(options.project);
  89. }
  90. return selectProjectInteractively();
  91. }
  92. exports.getOrPromptProject = getOrPromptProject;
  93. async function selectProjectInteractively(pageSize = MAXIMUM_PROMPT_LIST) {
  94. const { projects, nextPageToken } = await getFirebaseProjectPage(pageSize);
  95. if (projects.length === 0) {
  96. throw new error_1.FirebaseError("There are no Firebase projects associated with this account.");
  97. }
  98. if (nextPageToken) {
  99. logger_1.logger.debug(`Found more than ${projects.length} projects, selecting via prompt`);
  100. return selectProjectByPrompting();
  101. }
  102. return selectProjectFromList(projects);
  103. }
  104. async function selectProjectByPrompting() {
  105. const projectId = await (0, prompt_1.promptOnce)({
  106. type: "input",
  107. message: "Please input the project ID you would like to use:",
  108. });
  109. return await getFirebaseProject(projectId);
  110. }
  111. async function selectProjectFromList(projects = []) {
  112. const choices = projects
  113. .filter((p) => !!p)
  114. .map((p) => {
  115. return {
  116. name: p.projectId + (p.displayName ? ` (${p.displayName})` : ""),
  117. value: p.projectId,
  118. };
  119. })
  120. .sort((a, b) => a.name.localeCompare(b.name));
  121. if (choices.length >= 25) {
  122. utils.logBullet(`Don't want to scroll through all your projects? If you know your project ID, ` +
  123. `you can initialize it directly using ${clc.bold("firebase init --project <project_id>")}.\n`);
  124. }
  125. const projectId = await (0, prompt_1.promptOnce)({
  126. type: "list",
  127. name: "id",
  128. message: "Select a default Firebase project for this directory:",
  129. choices,
  130. });
  131. const project = projects.find((p) => p.projectId === projectId);
  132. if (!project) {
  133. throw new error_1.FirebaseError("Unexpected error. Project does not exist");
  134. }
  135. return project;
  136. }
  137. function getProjectId(cloudProject) {
  138. const resourceName = cloudProject.project;
  139. return resourceName.substring(resourceName.lastIndexOf("/") + 1);
  140. }
  141. async function promptAvailableProjectId() {
  142. const { projects, nextPageToken } = await getAvailableCloudProjectPage(MAXIMUM_PROMPT_LIST);
  143. if (projects.length === 0) {
  144. throw new error_1.FirebaseError("There are no available Google Cloud projects to add Firebase services.");
  145. }
  146. if (nextPageToken) {
  147. return await (0, prompt_1.promptOnce)({
  148. type: "input",
  149. message: "Please input the ID of the Google Cloud Project you would like to add Firebase:",
  150. });
  151. }
  152. else {
  153. const choices = projects
  154. .filter((p) => !!p)
  155. .map((p) => {
  156. const projectId = getProjectId(p);
  157. return {
  158. name: projectId + (p.displayName ? ` (${p.displayName})` : ""),
  159. value: projectId,
  160. };
  161. })
  162. .sort((a, b) => a.name.localeCompare(b.name));
  163. return await (0, prompt_1.promptOnce)({
  164. type: "list",
  165. name: "id",
  166. message: "Select the Google Cloud Platform project you would like to add Firebase:",
  167. choices,
  168. });
  169. }
  170. }
  171. exports.promptAvailableProjectId = promptAvailableProjectId;
  172. async function createCloudProject(projectId, options) {
  173. try {
  174. const client = new apiv2_1.Client({ urlPrefix: api.resourceManagerOrigin, apiVersion: "v1" });
  175. const data = {
  176. projectId,
  177. name: options.displayName || projectId,
  178. parent: options.parentResource,
  179. };
  180. const response = await client.request({
  181. method: "POST",
  182. path: "/projects",
  183. body: data,
  184. timeout: CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS,
  185. });
  186. const projectInfo = await (0, operation_poller_1.pollOperation)({
  187. pollerName: "Project Creation Poller",
  188. apiOrigin: api.resourceManagerOrigin,
  189. apiVersion: "v1",
  190. operationResourceName: response.body.name,
  191. });
  192. return projectInfo;
  193. }
  194. catch (err) {
  195. if (err.status === 409) {
  196. throw new error_1.FirebaseError(`Failed to create project because there is already a project with ID ${clc.bold(projectId)}. Please try again with a unique project ID.`, {
  197. exit: 2,
  198. original: err,
  199. });
  200. }
  201. else {
  202. throw new error_1.FirebaseError("Failed to create project. See firebase-debug.log for more info.", {
  203. exit: 2,
  204. original: err,
  205. });
  206. }
  207. }
  208. }
  209. exports.createCloudProject = createCloudProject;
  210. async function addFirebaseToCloudProject(projectId) {
  211. try {
  212. const response = await firebaseAPIClient.request({
  213. method: "POST",
  214. path: `/projects/${projectId}:addFirebase`,
  215. timeout: CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS,
  216. });
  217. const projectInfo = await (0, operation_poller_1.pollOperation)({
  218. pollerName: "Add Firebase Poller",
  219. apiOrigin: api.firebaseApiOrigin,
  220. apiVersion: "v1beta1",
  221. operationResourceName: response.body.name,
  222. });
  223. return projectInfo;
  224. }
  225. catch (err) {
  226. logger_1.logger.debug(err.message);
  227. throw new error_1.FirebaseError("Failed to add Firebase to Google Cloud Platform project. See firebase-debug.log for more info.", { exit: 2, original: err });
  228. }
  229. }
  230. exports.addFirebaseToCloudProject = addFirebaseToCloudProject;
  231. async function getProjectPage(apiResource, options) {
  232. const queryParams = {
  233. pageSize: `${options.pageSize}`,
  234. };
  235. if (options.pageToken) {
  236. queryParams.pageToken = options.pageToken;
  237. }
  238. const res = await firebaseAPIClient.request({
  239. method: "GET",
  240. path: apiResource,
  241. queryParams,
  242. timeout: TIMEOUT_MILLIS,
  243. skipLog: { resBody: true },
  244. });
  245. const projects = res.body[options.responseKey];
  246. const token = res.body.nextPageToken;
  247. return {
  248. projects: Array.isArray(projects) ? projects : [],
  249. nextPageToken: typeof token === "string" ? token : undefined,
  250. };
  251. }
  252. async function getFirebaseProjectPage(pageSize = PROJECT_LIST_PAGE_SIZE, pageToken) {
  253. let projectPage;
  254. try {
  255. projectPage = await getProjectPage("/projects", {
  256. responseKey: "results",
  257. pageSize,
  258. pageToken,
  259. });
  260. }
  261. catch (err) {
  262. logger_1.logger.debug(err.message);
  263. throw new error_1.FirebaseError("Failed to list Firebase projects. See firebase-debug.log for more info.", { exit: 2, original: err });
  264. }
  265. return projectPage;
  266. }
  267. exports.getFirebaseProjectPage = getFirebaseProjectPage;
  268. async function getAvailableCloudProjectPage(pageSize = PROJECT_LIST_PAGE_SIZE, pageToken) {
  269. try {
  270. return await getProjectPage("/availableProjects", {
  271. responseKey: "projectInfo",
  272. pageSize,
  273. pageToken,
  274. });
  275. }
  276. catch (err) {
  277. logger_1.logger.debug(err.message);
  278. throw new error_1.FirebaseError("Failed to list available Google Cloud Platform projects. See firebase-debug.log for more info.", { exit: 2, original: err });
  279. }
  280. }
  281. exports.getAvailableCloudProjectPage = getAvailableCloudProjectPage;
  282. async function listFirebaseProjects(pageSize) {
  283. const projects = [];
  284. let nextPageToken;
  285. do {
  286. const projectPage = await getFirebaseProjectPage(pageSize, nextPageToken);
  287. projects.push(...projectPage.projects);
  288. nextPageToken = projectPage.nextPageToken;
  289. } while (nextPageToken);
  290. return projects;
  291. }
  292. exports.listFirebaseProjects = listFirebaseProjects;
  293. async function getFirebaseProject(projectId) {
  294. try {
  295. const res = await firebaseAPIClient.request({
  296. method: "GET",
  297. path: `/projects/${projectId}`,
  298. timeout: TIMEOUT_MILLIS,
  299. });
  300. return res.body;
  301. }
  302. catch (err) {
  303. logger_1.logger.debug(err.message);
  304. throw new error_1.FirebaseError(`Failed to get Firebase project ${projectId}. ` +
  305. "Please make sure the project exists and your account has permission to access it.", { exit: 2, original: err });
  306. }
  307. }
  308. exports.getFirebaseProject = getFirebaseProject;