firebase-tools 10.1.3 → 10.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/lib/api.js +1 -0
  2. package/lib/apiv2.js +4 -0
  3. package/lib/auth.js +62 -25
  4. package/lib/commands/auth-import.js +1 -1
  5. package/lib/commands/ext-configure.js +1 -0
  6. package/lib/commands/ext-install.js +1 -0
  7. package/lib/commands/ext-uninstall.js +1 -0
  8. package/lib/commands/ext-update.js +1 -0
  9. package/lib/commands/functions-config-clone.js +1 -1
  10. package/lib/commands/functions-secrets-access.js +17 -0
  11. package/lib/commands/functions-secrets-destroy.js +40 -0
  12. package/lib/commands/functions-secrets-get.js +21 -0
  13. package/lib/commands/functions-secrets-prune.js +50 -0
  14. package/lib/commands/functions-secrets-set.js +46 -0
  15. package/lib/commands/index.js +7 -3
  16. package/lib/commands/login.js +1 -1
  17. package/lib/deploy/functions/backend.js +9 -1
  18. package/lib/deploy/functions/ensure.js +112 -0
  19. package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
  20. package/lib/deploy/functions/prepare.js +12 -18
  21. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -0
  22. package/lib/deploy/functions/runtimes/node/parseTriggers.js +12 -0
  23. package/lib/deploy/functions/validate.js +56 -1
  24. package/lib/deploy/hosting/client.js +9 -0
  25. package/lib/deploy/hosting/convertConfig.js +6 -0
  26. package/lib/deploy/hosting/index.js +5 -5
  27. package/lib/deploy/hosting/prepare.js +25 -25
  28. package/lib/deploy/hosting/release.js +21 -24
  29. package/lib/emulator/commandUtils.js +5 -1
  30. package/lib/emulator/controller.js +3 -1
  31. package/lib/emulator/downloadableEmulators.js +29 -12
  32. package/lib/emulator/emulatorLogger.js +7 -0
  33. package/lib/emulator/functionsEmulator.js +122 -80
  34. package/lib/emulator/functionsEmulatorRuntime.js +100 -83
  35. package/lib/emulator/functionsEmulatorShared.js +51 -1
  36. package/lib/emulator/functionsEmulatorShell.js +1 -2
  37. package/lib/emulator/functionsRuntimeWorker.js +1 -1
  38. package/lib/emulator/storage/apis/gcloud.js +2 -2
  39. package/lib/emulator/storage/files.js +8 -3
  40. package/lib/extensions/askUserForParam.js +1 -1
  41. package/lib/extensions/diagnose.js +56 -0
  42. package/lib/extensions/extensionsHelper.js +10 -17
  43. package/lib/extensions/listExtensions.js +2 -0
  44. package/lib/extensions/resolveSource.js +1 -53
  45. package/lib/extensions/secretsUtils.js +1 -1
  46. package/lib/extensions/updateHelper.js +0 -14
  47. package/lib/extensions/utils.js +4 -2
  48. package/lib/functions/env.js +5 -7
  49. package/lib/functions/secrets.js +112 -0
  50. package/lib/gcp/cloudfunctions.js +2 -2
  51. package/lib/gcp/secretManager.js +128 -46
  52. package/lib/gcp/storage.js +5 -3
  53. package/lib/hosting/functionsProxy.js +15 -5
  54. package/lib/previews.js +1 -1
  55. package/lib/responseToError.js +16 -7
  56. package/lib/serve/functions.js +2 -2
  57. package/lib/serve/hosting.js +1 -1
  58. package/lib/utils.js +6 -1
  59. package/npm-shrinkwrap.json +124 -45
  60. package/package.json +4 -4
  61. package/schema/firebase-config.json +27 -0
@@ -1,15 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const fs = require("fs");
4
- const types_1 = require("./types");
5
- const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
6
- const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
7
4
  const express = require("express");
8
5
  const path = require("path");
9
6
  const bodyParser = require("body-parser");
10
7
  const url_1 = require("url");
11
8
  const _ = require("lodash");
12
- let triggers;
9
+ const types_1 = require("./types");
10
+ const constants_1 = require("./constants");
11
+ const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
12
+ const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
13
+ let functionTrigger;
14
+ let FUNCTION_TARGET_NAME;
15
+ let FUNCTION_SIGNATURE;
13
16
  let developerPkgJSON;
14
17
  const dynamicImport = new Function("modulePath", "return import(modulePath)");
15
18
  function isFeatureEnabled(frb, feature) {
@@ -112,7 +115,7 @@ async function resolveDeveloperNodeModule(frb, name) {
112
115
  if (!isInPackageJSON) {
113
116
  return { declared: false, installed: false };
114
117
  }
115
- const resolveResult = await requireResolveAsync(name, { paths: [frb.cwd] }).catch(noOp);
118
+ const resolveResult = await requireResolveAsync(name, { paths: [process.cwd()] }).catch(noOp);
116
119
  if (!resolveResult) {
117
120
  return { declared: true, installed: false };
118
121
  }
@@ -160,7 +163,7 @@ function requirePackageJson(frb) {
160
163
  return developerPkgJSON;
161
164
  }
162
165
  try {
163
- const pkg = require(`${frb.cwd}/package.json`);
166
+ const pkg = require(`${process.cwd()}/package.json`);
164
167
  developerPkgJSON = {
165
168
  engines: pkg.engines || {},
166
169
  dependencies: pkg.dependencies || {},
@@ -321,7 +324,7 @@ function getDefaultConfig() {
321
324
  }
322
325
  function initializeRuntimeConfig(frb) {
323
326
  if (!process.env.CLOUD_RUNTIME_CONFIG) {
324
- const configPath = `${frb.cwd}/.runtimeconfig.json`;
327
+ const configPath = `${process.cwd()}/.runtimeconfig.json`;
325
328
  try {
326
329
  const configContent = fs.readFileSync(configPath, "utf8");
327
330
  if (configContent) {
@@ -359,7 +362,7 @@ async function initializeFirebaseAdminStubs(frb) {
359
362
  const defaultApp = makeProxiedFirebaseApp(frb, adminModuleTarget.initializeApp(defaultAppOptions));
360
363
  logDebug("initializeApp(DEFAULT)", defaultAppOptions);
361
364
  localFunctionsModule.app.setEmulatedAdminApp(defaultApp);
362
- if (frb.emulators.auth) {
365
+ if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
363
366
  if ((0, functionsEmulatorUtils_1.compareVersionStrings)(adminResolution.version, "9.3.0") < 0) {
364
367
  new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is running, but your 'firebase-admin' dependency is below version 9.3.0, so calls to Firebase Authentication will affect production.").log();
365
368
  }
@@ -377,16 +380,20 @@ async function initializeFirebaseAdminStubs(frb) {
377
380
  return defaultApp;
378
381
  })
379
382
  .when("firestore", (target) => {
380
- warnAboutFirestoreProd(frb);
383
+ warnAboutFirestoreProd();
381
384
  return Proxied.getOriginal(target, "firestore");
382
385
  })
383
386
  .when("database", (target) => {
384
- warnAboutDatabaseProd(frb);
387
+ warnAboutDatabaseProd();
385
388
  return Proxied.getOriginal(target, "database");
386
389
  })
387
390
  .when("auth", (target) => {
388
- warnAboutAuthProd(frb);
391
+ warnAboutAuthProd();
389
392
  return Proxied.getOriginal(target, "auth");
393
+ })
394
+ .when("storage", (target) => {
395
+ warnAboutStorageProd();
396
+ return Proxied.getOriginal(target, "storage");
390
397
  })
391
398
  .finalize();
392
399
  require.cache[adminResolution.resolution] = Object.assign(require.cache[adminResolution.resolution], {
@@ -401,37 +408,47 @@ function makeProxiedFirebaseApp(frb, original) {
401
408
  const appProxy = new Proxied(original);
402
409
  return appProxy
403
410
  .when("firestore", (target) => {
404
- warnAboutFirestoreProd(frb);
411
+ warnAboutFirestoreProd();
405
412
  return Proxied.getOriginal(target, "firestore");
406
413
  })
407
414
  .when("database", (target) => {
408
- warnAboutDatabaseProd(frb);
415
+ warnAboutDatabaseProd();
409
416
  return Proxied.getOriginal(target, "database");
410
417
  })
411
418
  .when("auth", (target) => {
412
- warnAboutAuthProd(frb);
419
+ warnAboutAuthProd();
413
420
  return Proxied.getOriginal(target, "auth");
421
+ })
422
+ .when("storage", (target) => {
423
+ warnAboutStorageProd();
424
+ return Proxied.getOriginal(target, "storage");
414
425
  })
415
426
  .finalize();
416
427
  }
417
- function warnAboutFirestoreProd(frb) {
418
- if (frb.emulators.firestore) {
428
+ function warnAboutFirestoreProd() {
429
+ if (process.env[constants_1.Constants.FIRESTORE_EMULATOR_HOST]) {
419
430
  return;
420
431
  }
421
432
  new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Cloud Firestore emulator is not running, so calls to Firestore will affect production.").log();
422
433
  }
423
- function warnAboutDatabaseProd(frb) {
424
- if (frb.emulators.database) {
434
+ function warnAboutDatabaseProd() {
435
+ if (process.env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST]) {
425
436
  return;
426
437
  }
427
438
  new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Realtime Database emulator is not running, so calls to Realtime Database will affect production.").log();
428
439
  }
429
- function warnAboutAuthProd(frb) {
430
- if (frb.emulators.auth) {
440
+ function warnAboutAuthProd() {
441
+ if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
431
442
  return;
432
443
  }
433
444
  new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is not running, so calls to Firebase Authentication will affect production.").log();
434
445
  }
446
+ function warnAboutStorageProd() {
447
+ if (process.env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST]) {
448
+ return;
449
+ }
450
+ new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Storage emulator is not running, so calls to Firebase Storage will affect production.").log();
451
+ }
435
452
  async function initializeFunctionsConfigHelper(frb) {
436
453
  const functionsResolution = await assertResolveDeveloperNodeModule(frb, "firebase-functions");
437
454
  const localFunctionsModule = require(functionsResolution.resolution);
@@ -467,7 +484,7 @@ async function initializeFunctionsConfigHelper(frb) {
467
484
  function rawBodySaver(req, res, buf) {
468
485
  req.rawBody = buf;
469
486
  }
470
- async function processHTTPS(frb, trigger) {
487
+ async function processHTTPS(frb) {
471
488
  const ephemeralServer = express();
472
489
  const functionRouter = express.Router();
473
490
  const socketPath = frb.socketPath;
@@ -479,7 +496,6 @@ async function processHTTPS(frb, trigger) {
479
496
  const handler = async (req, res) => {
480
497
  try {
481
498
  logDebug(`Ephemeral server handling ${req.method} request`);
482
- const func = trigger.getRawFunction();
483
499
  res.on("finish", () => {
484
500
  instance.close((err) => {
485
501
  if (err) {
@@ -490,7 +506,7 @@ async function processHTTPS(frb, trigger) {
490
506
  }
491
507
  });
492
508
  });
493
- await runHTTPS([req, res], func);
509
+ await runHTTPS([req, res]);
494
510
  }
495
511
  catch (err) {
496
512
  rejectEphemeralServer(err);
@@ -524,11 +540,11 @@ async function processHTTPS(frb, trigger) {
524
540
  instance.on("error", rejectEphemeralServer);
525
541
  });
526
542
  }
527
- async function processBackground(frb, trigger, signature) {
543
+ async function processBackground(frb, signature) {
528
544
  const proto = frb.proto;
529
545
  logDebug("ProcessBackground", proto);
530
546
  if (signature === "cloudevent") {
531
- return runCloudEvent(proto, trigger.getRawFunction());
547
+ return runCloudEvent(proto);
532
548
  }
533
549
  const data = proto.data;
534
550
  delete proto.data;
@@ -539,7 +555,7 @@ async function processBackground(frb, trigger, signature) {
539
555
  context.resource = context.resource.name;
540
556
  }
541
557
  }
542
- await runBackground({ data, context }, trigger.getRawFunction());
558
+ await runBackground({ data, context });
543
559
  }
544
560
  async function runFunction(func) {
545
561
  let caughtErr;
@@ -554,30 +570,30 @@ async function runFunction(func) {
554
570
  throw caughtErr;
555
571
  }
556
572
  }
557
- async function runBackground(proto, func) {
573
+ async function runBackground(proto) {
558
574
  logDebug("RunBackground", proto);
559
575
  await runFunction(() => {
560
- return func(proto.data, proto.context);
576
+ return functionTrigger(proto.data, proto.context);
561
577
  });
562
578
  }
563
- async function runCloudEvent(event, func) {
579
+ async function runCloudEvent(event) {
564
580
  logDebug("RunCloudEvent", event);
565
581
  await runFunction(() => {
566
- return func(event);
582
+ return functionTrigger(event);
567
583
  });
568
584
  }
569
- async function runHTTPS(args, func) {
585
+ async function runHTTPS(args) {
570
586
  if (args.length < 2) {
571
587
  throw new Error("Function must be passed 2 args.");
572
588
  }
573
589
  await runFunction(() => {
574
- return func(args[0], args[1]);
590
+ return functionTrigger(args[0], args[1]);
575
591
  });
576
592
  }
577
593
  async function moduleResolutionDetective(frb, error) {
578
594
  const clues = {
579
- tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [frb.cwd] }).catch(noOp),
580
- packageJSON: await requireAsync("./package.json", { paths: [frb.cwd] }).catch(noOp),
595
+ tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [process.cwd()] }).catch(noOp),
596
+ packageJSON: await requireAsync("./package.json", { paths: [process.cwd()] }).catch(noOp),
581
597
  };
582
598
  const isPotentially = {
583
599
  typescript: false,
@@ -595,45 +611,54 @@ async function moduleResolutionDetective(frb, error) {
595
611
  function logDebug(msg, data) {
596
612
  new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
597
613
  }
598
- async function invokeTrigger(frb, triggers) {
599
- if (!frb.triggerId) {
600
- throw new Error("frb.triggerId unexpectedly null");
601
- }
602
- new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${frb.triggerId}"`, {
614
+ async function invokeTrigger(frb) {
615
+ new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`, {
603
616
  frb,
604
617
  }).log();
605
- const trigger = triggers[frb.triggerId];
606
- logDebug("triggerDefinition", trigger.definition);
607
- const signature = (0, functionsEmulatorShared_1.getSignatureType)(trigger.definition);
608
- logDebug(`Running ${frb.triggerId} in signature ${signature}`);
618
+ logDebug(`Running ${FUNCTION_TARGET_NAME} in signature ${FUNCTION_SIGNATURE}`);
609
619
  let seconds = 0;
610
620
  const timerId = setInterval(() => {
611
621
  seconds++;
612
622
  }, 1000);
613
623
  let timeoutId;
614
624
  if (isFeatureEnabled(frb, "timeout")) {
625
+ let timeout = process.env.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS || "60";
626
+ if (timeout.endsWith("s")) {
627
+ timeout = timeout.slice(0, -1);
628
+ }
629
+ const timeoutMs = parseInt(timeout, 10) * 1000;
615
630
  timeoutId = setTimeout(() => {
616
- new types_1.EmulatorLog("WARN", "runtime-status", `Your function timed out after ~${trigger.definition.timeout || "60s"}. To configure this timeout, see
631
+ new types_1.EmulatorLog("WARN", "runtime-status", `Your function timed out after ~${timeout}s. To configure this timeout, see
617
632
  https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`).log();
618
633
  throw new Error("Function timed out.");
619
- }, trigger.timeoutMs);
634
+ }, timeoutMs);
620
635
  }
621
- switch (signature) {
636
+ switch (FUNCTION_SIGNATURE) {
622
637
  case "event":
623
638
  case "cloudevent":
624
- await processBackground(frb, triggers[frb.triggerId], signature);
639
+ await processBackground(frb, FUNCTION_SIGNATURE);
625
640
  break;
626
641
  case "http":
627
- await processHTTPS(frb, triggers[frb.triggerId]);
642
+ await processHTTPS(frb);
628
643
  break;
629
644
  }
630
645
  if (timeoutId) {
631
646
  clearTimeout(timeoutId);
632
647
  }
633
648
  clearInterval(timerId);
634
- new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${frb.triggerId}" in ~${Math.max(seconds, 1)}s`).log();
649
+ new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
635
650
  }
636
- async function initializeRuntime(frb, serializedFunctionTrigger, extensionTriggers) {
651
+ async function initializeRuntime(frb) {
652
+ FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
653
+ if (!FUNCTION_TARGET_NAME) {
654
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
655
+ await flushAndExit(1);
656
+ }
657
+ FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
658
+ if (!FUNCTION_SIGNATURE) {
659
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
660
+ await flushAndExit(1);
661
+ }
637
662
  logDebug(`Disabled runtime features: ${JSON.stringify(frb.disabled_features)}`);
638
663
  const verified = await verifyDeveloperNodeModules(frb);
639
664
  if (!verified) {
@@ -645,35 +670,33 @@ async function initializeRuntime(frb, serializedFunctionTrigger, extensionTrigge
645
670
  await initializeFunctionsConfigHelper(frb);
646
671
  await initializeFirebaseFunctionsStubs(frb);
647
672
  await initializeFirebaseAdminStubs(frb);
648
- let parsedDefinitions = [];
673
+ }
674
+ async function loadTrigger(frb, functionTarget, serializedFunctionTrigger) {
649
675
  let triggerModule;
650
676
  if (serializedFunctionTrigger) {
651
677
  triggerModule = eval(serializedFunctionTrigger)();
652
678
  }
653
679
  else {
654
680
  try {
655
- triggerModule = require(frb.cwd);
681
+ triggerModule = require(process.cwd());
656
682
  }
657
683
  catch (err) {
658
684
  if (err.code !== "ERR_REQUIRE_ESM") {
659
685
  await moduleResolutionDetective(frb, err);
660
- return;
686
+ throw err;
661
687
  }
662
- const modulePath = require.resolve(frb.cwd);
688
+ const modulePath = require.resolve(process.cwd());
663
689
  const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
664
690
  triggerModule = await dynamicImport(moduleURL);
665
691
  }
666
692
  }
667
- if (extensionTriggers) {
668
- parsedDefinitions = extensionTriggers;
669
- }
670
- else {
671
- require("../deploy/functions/runtimes/node/extractTriggers")(triggerModule, parsedDefinitions);
693
+ const maybeTrigger = functionTarget.split(".").reduce((mod, functionTargetPart) => {
694
+ return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
695
+ }, triggerModule);
696
+ if (!maybeTrigger) {
697
+ throw new Error(`Failed to find function ${functionTarget} in the loaded module`);
672
698
  }
673
- const triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(parsedDefinitions);
674
- const triggers = (0, functionsEmulatorShared_1.getEmulatedTriggersFromDefinitions)(triggerDefinitions, triggerModule);
675
- new types_1.EmulatorLog("SYSTEM", "triggers-parsed", "", { triggers, triggerDefinitions }).log();
676
- return triggers;
699
+ return maybeTrigger;
677
700
  }
678
701
  async function flushAndExit(code) {
679
702
  await types_1.EmulatorLog.waitForFlush();
@@ -693,28 +716,22 @@ async function handleMessage(message) {
693
716
  await flushAndExit(1);
694
717
  return;
695
718
  }
696
- if (!triggers) {
697
- const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
698
- const extensionTriggers = runtimeArgs.opts ? runtimeArgs.opts.extensionTriggers : undefined;
699
- triggers = await initializeRuntime(runtimeArgs.frb, serializedTriggers, extensionTriggers);
700
- }
701
- if (!triggers) {
702
- await flushAndExit(1);
703
- return;
704
- }
705
- if (!runtimeArgs.frb.triggerId) {
706
- await goIdle();
707
- return;
708
- }
709
- if (!triggers[runtimeArgs.frb.triggerId]) {
710
- new types_1.EmulatorLog("FATAL", "runtime-status", `Could not find trigger "${runtimeArgs.frb.triggerId}" in your functions directory.`).log();
711
- return;
712
- }
713
- else {
714
- logDebug(`Trigger "${runtimeArgs.frb.triggerId}" has been found, beginning invocation!`);
719
+ if (!functionTrigger) {
720
+ try {
721
+ await initializeRuntime(runtimeArgs.frb);
722
+ const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
723
+ functionTrigger = await loadTrigger(runtimeArgs.frb, FUNCTION_TARGET_NAME, serializedTriggers);
724
+ }
725
+ catch (e) {
726
+ logDebug(e);
727
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load trigger. This shouldn't happen: ${e.message}`).log();
728
+ await flushAndExit(1);
729
+ return;
730
+ }
715
731
  }
732
+ logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
716
733
  try {
717
- await invokeTrigger(runtimeArgs.frb, triggers);
734
+ await invokeTrigger(runtimeArgs.frb);
718
735
  if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
719
736
  await flushAndExit(0);
720
737
  }
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.EmulatedTrigger = exports.HttpConstants = void 0;
3
+ exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
4
4
  const _ = require("lodash");
5
5
  const os = require("os");
6
6
  const path = require("path");
7
7
  const fs = require("fs");
8
8
  const constants_1 = require("./constants");
9
+ const backend_1 = require("../deploy/functions/backend");
10
+ const proto_1 = require("../gcp/proto");
9
11
  const memoryLookup = {
10
12
  "128MB": 128,
11
13
  "256MB": 256,
@@ -44,6 +46,54 @@ class EmulatedTrigger {
44
46
  }
45
47
  }
46
48
  exports.EmulatedTrigger = EmulatedTrigger;
49
+ function emulatedFunctionsFromEndpoints(endpoints) {
50
+ const regionDefinitions = [];
51
+ for (const endpoint of endpoints) {
52
+ if (!endpoint.region) {
53
+ endpoint.region = "us-central1";
54
+ }
55
+ const def = {
56
+ entryPoint: endpoint.entryPoint,
57
+ platform: endpoint.platform,
58
+ region: endpoint.region,
59
+ name: endpoint.id,
60
+ id: `${endpoint.region}-${endpoint.id}`,
61
+ };
62
+ (0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
63
+ if ((0, backend_1.isHttpsTriggered)(endpoint)) {
64
+ def.httpsTrigger = endpoint.httpsTrigger;
65
+ }
66
+ else if ((0, backend_1.isEventTriggered)(endpoint)) {
67
+ const eventTrigger = endpoint.eventTrigger;
68
+ if (endpoint.platform === "gcfv1") {
69
+ def.eventTrigger = {
70
+ eventType: eventTrigger.eventType,
71
+ resource: eventTrigger.eventFilters.resource,
72
+ };
73
+ }
74
+ else {
75
+ const { resource, topic, bucket } = endpoint.eventTrigger.eventFilters;
76
+ const eventResource = resource || topic || bucket;
77
+ if (!eventResource) {
78
+ continue;
79
+ }
80
+ def.eventTrigger = {
81
+ eventType: eventTrigger.eventType,
82
+ resource: eventResource,
83
+ };
84
+ }
85
+ }
86
+ else if ((0, backend_1.isScheduleTriggered)(endpoint)) {
87
+ def.eventTrigger = { eventType: "pubsub", resource: "" };
88
+ def.schedule = endpoint.scheduleTrigger;
89
+ }
90
+ else {
91
+ }
92
+ regionDefinitions.push(def);
93
+ }
94
+ return regionDefinitions;
95
+ }
96
+ exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
47
97
  function emulatedFunctionsByRegion(definitions) {
48
98
  const regionDefinitions = [];
49
99
  for (const def of definitions) {
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FunctionsEmulatorShell = void 0;
4
4
  const uuid = require("uuid");
5
5
  const functionsEmulator_1 = require("./functionsEmulator");
6
- const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
7
6
  const utils = require("../utils");
8
7
  const logger_1 = require("../logger");
9
8
  const error_1 = require("../error");
@@ -43,7 +42,7 @@ class FunctionsEmulatorShell {
43
42
  auth: opts.auth,
44
43
  data,
45
44
  };
46
- this.emu.startFunctionRuntime(this.backend, trigger.id, trigger.name, (0, functionsEmulatorShared_1.getSignatureType)(trigger), proto);
45
+ this.emu.startFunctionRuntime(this.backend, trigger, proto);
47
46
  }
48
47
  getTrigger(name) {
49
48
  const result = this.triggers.find((trigger) => {
@@ -43,7 +43,7 @@ class RuntimeWorker {
43
43
  execute(frb, opts) {
44
44
  const execFrb = Object.assign({}, frb);
45
45
  if (!execFrb.socketPath) {
46
- execFrb.socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)(this.runtime.pid, execFrb.cwd);
46
+ execFrb.socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)(this.runtime.pid, this.runtime.cwd);
47
47
  this.log(`Assigning socketPath: ${execFrb.socketPath}`);
48
48
  }
49
49
  const args = { frb: execFrb, opts };
@@ -14,10 +14,10 @@ function createCloudEndpoints(emulator) {
14
14
  storageLayer.createBucket(req.params[0]);
15
15
  next();
16
16
  });
17
- gcloudStorageAPI.get("/b", (req, res) => {
17
+ gcloudStorageAPI.get("/b", async (req, res) => {
18
18
  res.json({
19
19
  kind: "storage#buckets",
20
- items: storageLayer.listBuckets(),
20
+ items: await storageLayer.listBuckets(),
21
21
  });
22
22
  });
23
23
  gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], (req, res) => {
@@ -19,6 +19,7 @@ const fse = require("fs-extra");
19
19
  const rimraf = require("rimraf");
20
20
  const cloudFunctions_1 = require("./cloudFunctions");
21
21
  const logger_1 = require("../../logger");
22
+ const adminSdkConfig_1 = require("../adminSdkConfig");
22
23
  class StoredFile {
23
24
  constructor(metadata, path) {
24
25
  this.metadata = metadata;
@@ -108,9 +109,13 @@ class StorageLayer {
108
109
  this._buckets.set(id, new metadata_1.CloudStorageBucketMetadata(id));
109
110
  }
110
111
  }
111
- listBuckets() {
112
+ async listBuckets() {
112
113
  if (this._buckets.size == 0) {
113
- this.createBucket("default-bucket");
114
+ let adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this._projectId);
115
+ if (!adminSdkConfig) {
116
+ adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this._projectId);
117
+ }
118
+ this.createBucket(adminSdkConfig.storageBucket);
114
119
  }
115
120
  return [...this._buckets.values()];
116
121
  }
@@ -355,7 +360,7 @@ class StorageLayer {
355
360
  const bucketsList = {
356
361
  buckets: [],
357
362
  };
358
- for (const b of this.listBuckets()) {
363
+ for (const b of await this.listBuckets()) {
359
364
  bucketsList.buckets.push({ id: b.id });
360
365
  }
361
366
  const bucketsFilePath = path.join(storageExportPath, "buckets.json");
@@ -199,7 +199,7 @@ async function generateSecretName(projectId, instanceId, paramName) {
199
199
  return secretName;
200
200
  }
201
201
  async function addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue) {
202
- const version = await secretManagerApi.addVersion(secret, secretValue);
202
+ const version = await secretManagerApi.addVersion(projectId, secret.name, secretValue);
203
203
  await secretsUtils.grantFirexServiceAgentSecretAdminRole(secret);
204
204
  return `projects/${version.secret.projectId}/secrets/${version.secret.name}/versions/${version.versionId}`;
205
205
  }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diagnose = void 0;
4
+ const extensionsHelper_1 = require("./extensionsHelper");
5
+ const getProjectNumber_1 = require("../getProjectNumber");
6
+ const utils = require("../utils");
7
+ const resourceManager = require("../gcp/resourceManager");
8
+ const extensionsApi_1 = require("./extensionsApi");
9
+ const prompt_1 = require("../prompt");
10
+ const logger_1 = require("../logger");
11
+ const error_1 = require("../error");
12
+ const SERVICE_AGENT_ROLE = "roles/firebasemods.serviceAgent";
13
+ async function diagnose(projectId) {
14
+ const projectNumber = await (0, getProjectNumber_1.getProjectNumber)({ projectId });
15
+ const firexSaProjectId = utils.envOverride("FIREBASE_EXTENSIONS_SA_PROJECT_ID", "gcp-sa-firebasemods");
16
+ const saEmail = `service-${projectNumber}@${firexSaProjectId}.iam.gserviceaccount.com`;
17
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Checking project IAM policy...");
18
+ await (0, extensionsApi_1.listInstances)(projectId);
19
+ let policy;
20
+ try {
21
+ policy = await resourceManager.getIamPolicy(projectId);
22
+ logger_1.logger.debug(policy);
23
+ }
24
+ catch (e) {
25
+ if (e instanceof error_1.FirebaseError && e.status === 403) {
26
+ throw new error_1.FirebaseError("Unable to get project IAM policy, permission denied (403). Please " +
27
+ "make sure you have sufficient project privileges or if this is a brand new project " +
28
+ "try again in a few minutes.");
29
+ }
30
+ throw e;
31
+ }
32
+ if (policy.bindings.find((b) => b.role === SERVICE_AGENT_ROLE && b.members.includes("serviceAccount:" + saEmail))) {
33
+ utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "Project IAM policy OK");
34
+ return true;
35
+ }
36
+ else {
37
+ utils.logWarning("Firebase Extensions Service Agent is missing a required IAM role " +
38
+ "`Firebase Extensions API Service Agent`.");
39
+ const fix = await (0, prompt_1.promptOnce)({
40
+ type: "confirm",
41
+ message: "Would you like to fix the issue by updating IAM policy to include Firebase " +
42
+ "Extensions Service Agent with role `Firebase Extensions API Service Agent`",
43
+ });
44
+ if (fix) {
45
+ policy.bindings.push({
46
+ role: SERVICE_AGENT_ROLE,
47
+ members: ["serviceAccount:" + saEmail],
48
+ });
49
+ await resourceManager.setIamPolicy(projectId, policy, "bindings");
50
+ utils.logSuccess("Project IAM policy updated successfully");
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ }
56
+ exports.diagnose = diagnose;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.getExtensionSourceFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
3
+ exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
4
4
  const _ = require("lodash");
5
5
  const clc = require("cli-color");
6
6
  const ora = require("ora");
@@ -16,6 +16,7 @@ const utils_1 = require("./utils");
16
16
  const functionsConfig_1 = require("../functionsConfig");
17
17
  const resolveSource_1 = require("./resolveSource");
18
18
  const error_1 = require("../error");
19
+ const diagnose_1 = require("./diagnose");
19
20
  const askUserForParam_1 = require("./askUserForParam");
20
21
  const ensureApiEnabled_1 = require("../ensureApiEnabled");
21
22
  const storage_1 = require("../gcp/storage");
@@ -382,22 +383,6 @@ async function deleteUploadedSource(objectPath) {
382
383
  }
383
384
  }
384
385
  }
385
- async function getExtensionSourceFromName(extensionName) {
386
- const officialExtensionRegex = /^[a-zA-Z\-]+[0-9@.]*$/;
387
- const existingSourceRegex = /projects\/.+\/sources\/.+/;
388
- if (officialExtensionRegex.test(extensionName)) {
389
- const [name, version] = extensionName.split("@");
390
- const registryEntry = await (0, resolveSource_1.resolveRegistryEntry)(name);
391
- const sourceUrl = (0, resolveSource_1.resolveSourceUrl)(registryEntry, name, version);
392
- return await (0, extensionsApi_1.getSource)(sourceUrl);
393
- }
394
- else if (existingSourceRegex.test(extensionName)) {
395
- logger_1.logger.info(`Fetching the source "${extensionName}"...`);
396
- return await (0, extensionsApi_1.getSource)(extensionName);
397
- }
398
- throw new error_1.FirebaseError(`Could not find an extension named '${extensionName}'. `);
399
- }
400
- exports.getExtensionSourceFromName = getExtensionSourceFromName;
401
386
  function getPublisherProjectFromName(publisherName) {
402
387
  const publisherNameRegex = /projects\/.+\/publisherProfile/;
403
388
  if (publisherNameRegex.test(publisherName)) {
@@ -520,3 +505,11 @@ async function confirm(args) {
520
505
  }
521
506
  }
522
507
  exports.confirm = confirm;
508
+ async function diagnoseAndFixProject(options) {
509
+ const projectId = (0, projectUtils_1.needProjectId)(options);
510
+ const ok = await (0, diagnose_1.diagnose)(projectId);
511
+ if (!ok) {
512
+ throw new error_1.FirebaseError("Unable to proceed until all issues are resolved.");
513
+ }
514
+ }
515
+ exports.diagnoseAndFixProject = diagnoseAndFixProject;