firebase-tools 13.14.2 → 13.15.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/bin/firebase.js +2 -4
  2. package/lib/commands/deploy.js +10 -2
  3. package/lib/commands/ext-configure.js +1 -6
  4. package/lib/commands/ext-dev-init.js +2 -4
  5. package/lib/commands/ext-dev-upload.js +4 -6
  6. package/lib/commands/ext-info.js +3 -5
  7. package/lib/commands/ext-install.js +0 -5
  8. package/lib/commands/ext-uninstall.js +0 -5
  9. package/lib/commands/ext-update.js +0 -5
  10. package/lib/commands/firestore-databases-create.js +19 -1
  11. package/lib/commands/functions-secrets-set.js +9 -6
  12. package/lib/commands/hosting-channel-create.js +1 -1
  13. package/lib/commands/hosting-channel-delete.js +1 -1
  14. package/lib/commands/hosting-channel-deploy.js +1 -1
  15. package/lib/commands/hosting-clone.js +1 -2
  16. package/lib/dataconnect/fileUtils.js +63 -1
  17. package/lib/dataconnect/provisionCloudSql.js +5 -1
  18. package/lib/dataconnect/types.js +8 -1
  19. package/lib/deploy/extensions/planner.js +46 -1
  20. package/lib/deploy/extensions/prepare.js +99 -23
  21. package/lib/deploy/functions/build.js +5 -5
  22. package/lib/deploy/functions/deploy.js +12 -12
  23. package/lib/deploy/functions/params.js +5 -3
  24. package/lib/deploy/functions/prepare.js +16 -1
  25. package/lib/deploy/functions/release/index.js +4 -0
  26. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  27. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
  28. package/lib/emulator/dataconnectEmulator.js +2 -0
  29. package/lib/emulator/downloadableEmulators.js +11 -12
  30. package/lib/emulator/functionsEmulator.js +8 -1
  31. package/lib/extensions/askUserForEventsConfig.js +18 -8
  32. package/lib/extensions/askUserForParam.js +3 -2
  33. package/lib/extensions/change-log.js +2 -4
  34. package/lib/extensions/displayExtensionInfo.js +5 -10
  35. package/lib/extensions/extensionsApi.js +1 -1
  36. package/lib/extensions/extensionsHelper.js +47 -10
  37. package/lib/extensions/localHelper.js +1 -1
  38. package/lib/extensions/provisioningHelper.js +1 -1
  39. package/lib/extensions/refs.js +26 -11
  40. package/lib/extensions/runtimes/common.js +75 -0
  41. package/lib/extensions/types.js +56 -1
  42. package/lib/extensions/updateHelper.js +1 -2
  43. package/lib/extensions/warnings.js +2 -7
  44. package/lib/firestore/api.js +8 -7
  45. package/lib/firestore/pretty-print.js +21 -1
  46. package/lib/firestore/pretty-print.test.js +8 -0
  47. package/lib/frameworks/constants.js +1 -1
  48. package/lib/frameworks/next/constants.js +1 -1
  49. package/lib/frameworks/next/index.js +26 -14
  50. package/lib/frameworks/next/utils.js +45 -1
  51. package/lib/init/features/dataconnect/index.js +29 -15
  52. package/lib/init/features/dataconnect/sdk.js +72 -56
  53. package/lib/prompt.js +22 -1
  54. package/lib/rulesDeploy.js +14 -12
  55. package/package.json +3 -3
  56. package/templates/init/dataconnect/connector.yaml +5 -3
  57. package/templates/init/dataconnect/dataconnect.yaml +5 -5
  58. package/templates/init/dataconnect/mutations.gql +44 -29
  59. package/templates/init/dataconnect/queries.gql +66 -38
  60. package/templates/init/dataconnect/schema.gql +38 -21
  61. package/lib/extensions/billingMigrationHelper.js +0 -61
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.doSetup = void 0;
3
+ exports.actuate = exports.doSetup = void 0;
4
4
  const path_1 = require("path");
5
5
  const clc = require("colorette");
6
6
  const prompt_1 = require("../../../prompt");
@@ -20,7 +20,8 @@ const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/sche
20
20
  const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/queries.gql");
21
21
  const MUTATIONS_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/mutations.gql");
22
22
  const defaultConnector = {
23
- id: "default-connector",
23
+ id: "default",
24
+ path: "./connector",
24
25
  files: [
25
26
  {
26
27
  path: "queries.gql",
@@ -33,6 +34,13 @@ const defaultConnector = {
33
34
  ],
34
35
  };
35
36
  async function doSetup(setup, config) {
37
+ const info = await askQuestions(setup, config);
38
+ await actuate(setup, config, info);
39
+ logger_1.logger.info("");
40
+ (0, utils_1.logSuccess)(`If you'd like to generate an SDK for your new connector, run ${clc.bold("firebase init dataconnect:sdk")}`);
41
+ }
42
+ exports.doSetup = doSetup;
43
+ async function askQuestions(setup, config) {
36
44
  var _a, _b, _c;
37
45
  let info = {
38
46
  serviceId: "",
@@ -43,6 +51,7 @@ async function doSetup(setup, config) {
43
51
  isNewDatabase: false,
44
52
  connectors: [defaultConnector],
45
53
  schemaGql: [],
54
+ shouldProvisionCSQL: false,
46
55
  };
47
56
  info = await promptForService(setup, info);
48
57
  if (info.cloudSqlInstanceId === "") {
@@ -59,13 +68,17 @@ async function doSetup(setup, config) {
59
68
  default: defaultConnectionString,
60
69
  });
61
70
  setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
62
- await writeFiles(config, info);
63
- if (setup.projectId &&
71
+ info.shouldProvisionCSQL = !!(setup.projectId &&
64
72
  (info.isNewInstance || info.isNewDatabase) &&
65
73
  (await (0, prompt_1.confirm)({
66
74
  message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
67
75
  default: true,
68
- }))) {
76
+ })));
77
+ return info;
78
+ }
79
+ async function actuate(setup, config, info) {
80
+ await writeFiles(config, info);
81
+ if (setup.projectId && info.shouldProvisionCSQL) {
69
82
  await (0, provisionCloudSql_1.provisionCloudSql)({
70
83
  projectId: setup.projectId,
71
84
  locationId: info.locationId,
@@ -75,13 +88,12 @@ async function doSetup(setup, config) {
75
88
  waitForCreation: false,
76
89
  });
77
90
  }
78
- logger_1.logger.info("");
79
- (0, utils_1.logSuccess)(`If you'd like to generate an SDK for your new connector, run ${clc.bold("firebase init dataconnect:sdk")}`);
80
91
  }
81
- exports.doSetup = doSetup;
92
+ exports.actuate = actuate;
82
93
  async function writeFiles(config, info) {
83
94
  const dir = config.get("dataconnect.source") || "dataconnect";
84
- const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorIds: info.connectors.map((c) => `"./${c.id}"`).join(", ") }));
95
+ console.log(dir);
96
+ const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: info.connectors.map((c) => c.path) }));
85
97
  config.set("dataconnect", { source: dir });
86
98
  await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
87
99
  if (info.schemaGql.length) {
@@ -100,9 +112,9 @@ async function writeFiles(config, info) {
100
112
  async function writeConnectorFiles(config, connectorInfo) {
101
113
  const subbedConnectorYaml = subConnectorYamlValues({ connectorId: connectorInfo.id });
102
114
  const dir = config.get("dataconnect.source") || "dataconnect";
103
- await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.id, "connector.yaml"), subbedConnectorYaml);
115
+ await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, "connector.yaml"), subbedConnectorYaml);
104
116
  for (const f of connectorInfo.files) {
105
- await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.id, f.path), f.content);
117
+ await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, f.path), f.content);
106
118
  }
107
119
  }
108
120
  function subDataconnectYamlValues(replacementValues) {
@@ -110,12 +122,12 @@ function subDataconnectYamlValues(replacementValues) {
110
122
  serviceId: "__serviceId__",
111
123
  cloudSqlDatabase: "__cloudSqlDatabase__",
112
124
  cloudSqlInstanceId: "__cloudSqlInstanceId__",
113
- connectorIds: "__connectorIds__",
125
+ connectorDirs: "__connectorDirs__",
114
126
  locationId: "__location__",
115
127
  };
116
128
  let replaced = DATACONNECT_YAML_TEMPLATE;
117
129
  for (const [k, v] of Object.entries(replacementValues)) {
118
- replaced = replaced.replace(replacements[k], v);
130
+ replaced = replaced.replace(replacements[k], JSON.stringify(v));
119
131
  }
120
132
  return replaced;
121
133
  }
@@ -125,7 +137,7 @@ function subConnectorYamlValues(replacementValues) {
125
137
  };
126
138
  let replaced = CONNECTOR_YAML_TEMPLATE;
127
139
  for (const [k, v] of Object.entries(replacementValues)) {
128
- replaced = replaced.replace(replacements[k], v);
140
+ replaced = replaced.replace(replacements[k], JSON.stringify(v));
129
141
  }
130
142
  return replaced;
131
143
  }
@@ -170,8 +182,10 @@ async function promptForService(setup, info) {
170
182
  const connectors = await (0, client_1.listConnectors)(choice.service.name);
171
183
  if (connectors.length) {
172
184
  info.connectors = connectors.map((c) => {
185
+ const id = c.name.split("/").pop();
173
186
  return {
174
- id: c.name.split("/").pop(),
187
+ id,
188
+ path: `./${id}`,
175
189
  files: c.source.files || [],
176
190
  };
177
191
  });
@@ -4,22 +4,22 @@ exports.actuate = exports.doSetup = void 0;
4
4
  const yaml = require("yaml");
5
5
  const fs = require("fs");
6
6
  const clc = require("colorette");
7
+ const path = require("path");
7
8
  const prompt_1 = require("../../../prompt");
8
9
  const fileUtils_1 = require("../../../dataconnect/fileUtils");
9
10
  const load_1 = require("../../../dataconnect/load");
10
- const logger_1 = require("../../../logger");
11
+ const types_1 = require("../../../dataconnect/types");
11
12
  const dataconnectEmulator_1 = require("../../../emulator/dataconnectEmulator");
12
13
  const error_1 = require("../../../error");
13
- const IOS = "ios";
14
- const WEB = "web";
15
- const ANDROID = "android";
14
+ const lodash_1 = require("lodash");
15
+ const utils_1 = require("../../../utils");
16
16
  async function doSetup(setup, config) {
17
17
  const sdkInfo = await askQuestions(setup, config);
18
18
  await actuate(sdkInfo, setup.projectId);
19
19
  }
20
20
  exports.doSetup = doSetup;
21
21
  async function askQuestions(setup, config) {
22
- var _a, _b, _c, _d, _e, _f, _g, _h;
22
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
23
23
  const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(config);
24
24
  const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(setup.projectId || "", config, c.source)));
25
25
  const connectorChoices = serviceInfos
@@ -40,67 +40,78 @@ async function askQuestions(setup, config) {
40
40
  type: "list",
41
41
  choices: connectorChoices,
42
42
  });
43
- const platforms = await (0, prompt_1.promptOnce)({
44
- message: "Which platforms do you want to set up a generated SDK for?",
45
- type: "checkbox",
46
- choices: [
47
- { name: "iOS (Swift)", value: IOS },
48
- { name: "Web (JavaScript)", value: WEB },
49
- { name: "Androd (Kotlin)", value: ANDROID },
50
- ],
51
- });
43
+ let targetPlatform = types_1.Platform.UNDETERMINED;
44
+ let appDir;
45
+ const cwdPlatformGuess = await (0, fileUtils_1.getPlatformFromFolder)(process.cwd());
46
+ if (cwdPlatformGuess !== types_1.Platform.UNDETERMINED) {
47
+ (0, utils_1.logSuccess)(`Detected ${cwdPlatformGuess} app in current directory ${process.cwd()}`);
48
+ targetPlatform = cwdPlatformGuess;
49
+ appDir = process.cwd();
50
+ }
51
+ else {
52
+ (0, utils_1.logBullet)(`Couldn't automatically detect your app directory.`);
53
+ appDir = await (0, prompt_1.promptForDirectory)({
54
+ config,
55
+ message: "Where is your app directory?",
56
+ });
57
+ const platformGuess = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
58
+ if (platformGuess !== types_1.Platform.UNDETERMINED) {
59
+ (0, utils_1.logSuccess)(`Detected ${platformGuess} app in directory ${appDir}`);
60
+ targetPlatform = platformGuess;
61
+ }
62
+ else {
63
+ (0, utils_1.logBullet)("Couldn't automatically detect your app's platform.");
64
+ targetPlatform = await (0, prompt_1.promptOnce)({
65
+ message: "Which platform do you want to set up a generated SDK for?",
66
+ type: "list",
67
+ choices: [
68
+ { name: "iOS (Swift)", value: types_1.Platform.IOS },
69
+ { name: "Web (JavaScript)", value: types_1.Platform.WEB },
70
+ { name: "Android (Kotlin)", value: types_1.Platform.ANDROID },
71
+ ],
72
+ });
73
+ }
74
+ }
52
75
  const newConnectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml));
53
76
  if (!newConnectorYaml.generate) {
54
77
  newConnectorYaml.generate = {};
55
78
  }
56
- if (platforms.includes(IOS)) {
57
- const outputDir = await (0, prompt_1.promptOnce)({
58
- message: `What directory do you want to write your Swift SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
59
- type: "input",
60
- default: ((_a = newConnectorYaml.generate.swiftSdk) === null || _a === void 0 ? void 0 : _a.outputDir) ||
61
- `./../.dataconnect/generated/${newConnectorYaml.connectorId}/swift-sdk`,
62
- });
63
- const swiftSdk = { outputDir };
79
+ let displayIOSWarning = false;
80
+ if (targetPlatform === types_1.Platform.IOS) {
81
+ const outputDir = ((_a = newConnectorYaml.generate.swiftSdk) === null || _a === void 0 ? void 0 : _a.outputDir) ||
82
+ path.relative(connectorInfo.directory, path.join(appDir, `generated/swift`));
83
+ const pkg = (_c = (_b = newConnectorYaml.generate.swiftSdk) === null || _b === void 0 ? void 0 : _b.package) !== null && _c !== void 0 ? _c : (0, lodash_1.upperFirst)((0, lodash_1.camelCase)(newConnectorYaml.connectorId));
84
+ const swiftSdk = { outputDir, package: pkg };
64
85
  newConnectorYaml.generate.swiftSdk = swiftSdk;
86
+ displayIOSWarning = true;
65
87
  }
66
- if (platforms.includes(WEB)) {
67
- const outputDir = await (0, prompt_1.promptOnce)({
68
- message: `What directory do you want to write your JavaScript SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
69
- type: "input",
70
- default: ((_b = newConnectorYaml.generate.javascriptSdk) === null || _b === void 0 ? void 0 : _b.outputDir) ||
71
- `./../.dataconnect/generated/${newConnectorYaml.connectorId}/javascript-sdk`,
72
- });
73
- const pkg = await (0, prompt_1.promptOnce)({
74
- message: "What package name do you want to use for your JavaScript SDK?",
75
- type: "input",
76
- default: (_d = (_c = newConnectorYaml.generate.javascriptSdk) === null || _c === void 0 ? void 0 : _c.package) !== null && _d !== void 0 ? _d : `@firebasegen/${connectorInfo.connectorYaml.connectorId}`,
77
- });
78
- const packageJSONDir = await (0, prompt_1.promptOnce)({
79
- message: "Which directory contains the package.json that you would like to add the JavaScript SDK dependency to? (Leave blank to skip)",
80
- type: "input",
81
- default: (_e = newConnectorYaml.generate.javascriptSdk) === null || _e === void 0 ? void 0 : _e.packageJSONDir,
82
- });
88
+ if (targetPlatform === types_1.Platform.WEB) {
89
+ const outputDir = ((_d = newConnectorYaml.generate.javascriptSdk) === null || _d === void 0 ? void 0 : _d.outputDir) ||
90
+ path.relative(connectorInfo.directory, path.join(appDir, `generated/javascript/${newConnectorYaml.connectorId}`));
91
+ const pkg = (_f = (_e = newConnectorYaml.generate.javascriptSdk) === null || _e === void 0 ? void 0 : _e.package) !== null && _f !== void 0 ? _f : `@firebasegen/${connectorInfo.connectorYaml.connectorId}`;
83
92
  const javascriptSdk = {
84
93
  outputDir,
85
94
  package: pkg,
86
95
  };
87
- if (packageJSONDir) {
88
- javascriptSdk.packageJSONDir = packageJSONDir;
96
+ if ((await (0, fileUtils_1.directoryHasPackageJson)(appDir)) &&
97
+ (await (0, prompt_1.confirm)({
98
+ message: "Would you like to add a dependency on the generated SDK to your package.json?",
99
+ }))) {
100
+ javascriptSdk.packageJsonDir = path.relative(connectorInfo.directory, appDir);
89
101
  }
90
102
  newConnectorYaml.generate.javascriptSdk = javascriptSdk;
91
103
  }
92
- if (platforms.includes(ANDROID)) {
93
- const outputDir = await (0, prompt_1.promptOnce)({
94
- message: `What directory do you want to write your Kotlin SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
95
- type: "input",
96
- default: ((_f = newConnectorYaml.generate.kotlinSdk) === null || _f === void 0 ? void 0 : _f.outputDir) ||
97
- `./../.dataconnect/generated/${newConnectorYaml.connectorId}/kotlin-sdk/src/main/kotlin/${newConnectorYaml.connectorId}`,
98
- });
99
- const pkg = await (0, prompt_1.promptOnce)({
100
- message: "What package name do you want to use for your Kotlin SDK?",
101
- type: "input",
102
- default: (_h = (_g = newConnectorYaml.generate.kotlinSdk) === null || _g === void 0 ? void 0 : _g.package) !== null && _h !== void 0 ? _h : `com.google.firebase.dataconnect.connectors.${connectorInfo.connectorYaml.connectorId}`,
103
- });
104
+ if (targetPlatform === types_1.Platform.ANDROID) {
105
+ let baseDir = path.join(appDir, `generated/kotlin`);
106
+ for (const candidateSubdir of ["app/src/main/java", "app/src/main/kotlin"]) {
107
+ const candidateDir = path.join(appDir, candidateSubdir);
108
+ if (fs.existsSync(candidateDir)) {
109
+ baseDir = candidateDir;
110
+ }
111
+ }
112
+ const outputDir = ((_g = newConnectorYaml.generate.kotlinSdk) === null || _g === void 0 ? void 0 : _g.outputDir) ||
113
+ path.relative(connectorInfo.directory, baseDir);
114
+ const pkg = (_j = (_h = newConnectorYaml.generate.kotlinSdk) === null || _h === void 0 ? void 0 : _h.package) !== null && _j !== void 0 ? _j : `connectors.${(0, lodash_1.snakeCase)(connectorInfo.connectorYaml.connectorId)}`;
104
115
  const kotlinSdk = {
105
116
  outputDir,
106
117
  package: pkg,
@@ -113,18 +124,23 @@ async function askQuestions(setup, config) {
113
124
  default: true,
114
125
  })));
115
126
  const connectorYamlContents = yaml.stringify(newConnectorYaml);
116
- return { connectorYamlContents, connectorInfo, shouldGenerate };
127
+ connectorInfo.connectorYaml = newConnectorYaml;
128
+ return { connectorYamlContents, connectorInfo, shouldGenerate, displayIOSWarning };
117
129
  }
118
130
  async function actuate(sdkInfo, projectId) {
131
+ var _a;
119
132
  const connectorYamlPath = `${sdkInfo.connectorInfo.directory}/connector.yaml`;
120
133
  fs.writeFileSync(connectorYamlPath, sdkInfo.connectorYamlContents, "utf8");
121
- logger_1.logger.info(`Wrote new config to ${connectorYamlPath}`);
134
+ (0, utils_1.logBullet)(`Wrote new config to ${connectorYamlPath}`);
122
135
  if (projectId && sdkInfo.shouldGenerate) {
123
136
  await dataconnectEmulator_1.DataConnectEmulator.generate({
124
137
  configDir: sdkInfo.connectorInfo.directory,
125
138
  connectorId: sdkInfo.connectorInfo.connectorYaml.connectorId,
126
139
  });
127
- logger_1.logger.info(`Generated SDK code for ${sdkInfo.connectorInfo.connectorYaml.connectorId}`);
140
+ (0, utils_1.logBullet)(`Generated SDK code for ${sdkInfo.connectorInfo.connectorYaml.connectorId}`);
141
+ }
142
+ if (((_a = sdkInfo.connectorInfo.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.swiftSdk) && sdkInfo.displayIOSWarning) {
143
+ (0, utils_1.logBullet)(clc.bold("Please follow the instructions here to add your generated sdk to your XCode project:\n\thttps://firebase.google.com/docs/data-connect/gp/ios-sdk#set-client"));
128
144
  }
129
145
  }
130
146
  exports.actuate = actuate;
package/lib/prompt.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.confirm = exports.promptOnce = exports.prompt = void 0;
3
+ exports.promptForDirectory = exports.confirm = exports.promptOnce = exports.prompt = void 0;
4
4
  const inquirer = require("inquirer");
5
+ const fsutils_1 = require("./fsutils");
5
6
  const error_1 = require("./error");
7
+ const logger_1 = require("./logger");
6
8
  inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
7
9
  async function prompt(options, questions) {
8
10
  const prompts = [];
@@ -49,3 +51,22 @@ async function confirm(args) {
49
51
  }
50
52
  }
51
53
  exports.confirm = confirm;
54
+ async function promptForDirectory(args) {
55
+ let dir = "";
56
+ while (!dir) {
57
+ const target = args.config.path(await promptOnce({
58
+ message: "Where is your app directory?",
59
+ }));
60
+ if ((0, fsutils_1.fileExistsSync)(target)) {
61
+ logger_1.logger.error(`Expected a directory, but ${target} is a file. Please provide a path to a directory.`);
62
+ }
63
+ else if (!(0, fsutils_1.dirExistsSync)(target)) {
64
+ logger_1.logger.error(`Directory ${target} not found. Please provide a path to a directory`);
65
+ }
66
+ else {
67
+ dir = target;
68
+ }
69
+ }
70
+ return dir;
71
+ }
72
+ exports.promptForDirectory = promptForDirectory;
@@ -71,12 +71,13 @@ class RulesDeploy {
71
71
  if (await (0, resourceManager_1.serviceAccountHasRoles)(projectNumber, saEmail, [CROSS_SERVICE_RULES_ROLE], true)) {
72
72
  return;
73
73
  }
74
- const addRole = await (0, prompt_1.promptOnce)({
75
- type: "confirm",
76
- name: "rulesRole",
77
- message: `Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?`,
78
- default: true,
79
- }, this.options);
74
+ const addRole = this.options.force ||
75
+ (await (0, prompt_1.promptOnce)({
76
+ type: "confirm",
77
+ name: "rulesRole",
78
+ message: `Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?`,
79
+ default: true,
80
+ }, this.options));
80
81
  if (addRole) {
81
82
  await (0, resourceManager_1.addServiceAccountToRoles)(projectNumber, saEmail, [CROSS_SERVICE_RULES_ROLE], true);
82
83
  utils.logLabeledBullet(RulesetType[this.type], "updated service account for cross-service rules...");
@@ -118,12 +119,13 @@ class RulesDeploy {
118
119
  utils.logLabeledBullet(RulesetType[this.type], "quota exceeded error while uploading rules");
119
120
  const history = await gcp.rules.listAllRulesets(this.options.project);
120
121
  if (history.length > RULESET_COUNT_LIMIT) {
121
- const confirm = await (0, prompt_1.promptOnce)({
122
- type: "confirm",
123
- name: "force",
124
- message: `You have ${history.length} rules, do you want to delete the oldest ${RULESETS_TO_GC} to free up space?`,
125
- default: false,
126
- }, this.options);
122
+ const confirm = this.options.force ||
123
+ (await (0, prompt_1.promptOnce)({
124
+ type: "confirm",
125
+ name: "force",
126
+ message: `You have ${history.length} rules, do you want to delete the oldest ${RULESETS_TO_GC} to free up space?`,
127
+ default: false,
128
+ }, this.options));
127
129
  if (confirm) {
128
130
  const releases = await gcp.rules.listAllReleases(this.options.project);
129
131
  const unreleased = history.filter((ruleset) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.14.2",
3
+ "version": "13.15.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -94,8 +94,8 @@
94
94
  "leven": "^3.1.0",
95
95
  "libsodium-wrappers": "^0.7.10",
96
96
  "lodash": "^4.17.21",
97
- "marked": "^4.0.14",
98
- "marked-terminal": "^5.1.1",
97
+ "marked": "^13.0.2",
98
+ "marked-terminal": "^7.0.0",
99
99
  "mime": "^2.5.2",
100
100
  "minimatch": "^3.0.4",
101
101
  "morgan": "^1.10.0",
@@ -1,13 +1,15 @@
1
- connectorId: "__connectorId__"
2
- authMode: "PUBLIC"
1
+ connectorId: __connectorId__
2
+ authMode: "PUBLIC"
3
3
  ## ## Here's an example of how to add generated SDKs.
4
4
  ## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
5
5
  # generate:
6
6
  # javascriptSdk:
7
7
  # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
8
8
  # package: "@firebasegen/my-connector"
9
- # packageJSONDir: < Optional. Path to your Javascript app's package.json>
9
+ # packageJsonDir: < Optional. Path to your Javascript app's package.json>
10
10
  # swiftSdk:
11
11
  # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
12
+ # package: "firebasegen/default"
12
13
  # kotlinSdk:
13
14
  # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
15
+ # package: connectors.default
@@ -1,11 +1,11 @@
1
1
  specVersion: "v1alpha"
2
- serviceId: "__serviceId__"
3
- location: "__location__"
2
+ serviceId: __serviceId__
3
+ location: __location__
4
4
  schema:
5
5
  source: "./schema"
6
6
  datasource:
7
7
  postgresql:
8
- database: "__cloudSqlDatabase__"
8
+ database: __cloudSqlDatabase__
9
9
  cloudSql:
10
- instanceId: "__cloudSqlInstanceId__"
11
- connectorDirs: [__connectorIds__]
10
+ instanceId: __cloudSqlInstanceId__
11
+ connectorDirs: __connectorDirs__
@@ -1,35 +1,50 @@
1
- # # Example mutations for a simple email app
1
+ # # Example mutations for a simple movie app
2
2
 
3
- # # Logged in user can create their own account.
4
- # mutation CreateUser($name: String!, $address: String!) @auth(level: USER) {
5
- # # <type>_insert lets you create a new row in your table.
6
- # user_insert(data: {
7
- # # Server values let your service populate sensitive data.
8
- # # Users can only setup their own account.
9
- # uid_expr: "auth.uid",
10
- # name: $name,
11
- # address: $address
12
- # })
3
+ # # Create a movie based on user input
4
+ # mutation CreateMovie(
5
+ # $title: String!
6
+ # $genre: String!
7
+ # $imageUrl: String!
8
+ # ) @auth(level: USER_EMAIL_VERIFIED) {
9
+ # movie_insert(
10
+ # data: {
11
+ # title: $title
12
+ # genre: $genre
13
+ # imageUrl: $imageUrl
14
+ # }
15
+ # )
13
16
  # }
14
17
 
15
- # # Logged in user can send emails from their account.
16
- # mutation CreateEmail($content: String, $subject: String) @auth(level: USER) {
17
- # email_insert(data: {
18
- # # The request variable name doesn't have to match the field name.
19
- # text: $content,
20
- # subject: $subject,
21
- # # Server values let your service populate sensitive data.
22
- # # Users are only allowed to create emails sent from their account.
23
- # fromUid_expr: "auth.uid",
24
- # # Server values let your service populate data for you
25
- # # Here, we use sent_date: { today: true } to set 'sent' to today's date.
26
- # sent_date: { today: true }
27
- # })
18
+ # # Upsert (update or insert) a user's username based on their auth.uid
19
+ # mutation UpsertUser($username: String!) @auth(level: USER) {
20
+ # user_upsert(
21
+ # data: {
22
+ # id_expr: "auth.uid"
23
+ # username: $username
24
+ # }
25
+ # )
28
26
  # }
29
27
 
30
- # mutation CreateRecipient($emailId: UUID) @auth(level: USER) {
31
- # recipient_insert(data: {
32
- # emailId: $emailId,
33
- # userUid_expr: "auth.uid"
34
- # })
28
+ # # Add a review for a movie
29
+ # mutation AddReview(
30
+ # $movieId: UUID!
31
+ # $rating: Int!
32
+ # $reviewText: String!
33
+ # ) @auth(level: USER) {
34
+ # review_upsert(
35
+ # data: {
36
+ # userId_expr: "auth.uid"
37
+ # movieId: $movieId
38
+ # rating: $rating
39
+ # reviewText: $reviewText
40
+ # # reviewDate defaults to today in the schema. No need to set it manually.
41
+ # }
42
+ # )
43
+ # }
44
+
45
+ # # Logged in user can delete their review for a movie
46
+ # mutation DeleteReview(
47
+ # $movieId: UUID!
48
+ # ) @auth(level: USER) {
49
+ # review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
35
50
  # }
@@ -1,56 +1,84 @@
1
- # # Example queries for a simple email app.
1
+ # # Example queries for a simple movie app.
2
2
 
3
3
  # # @auth() directives control who can call each operation.
4
- # # Only admins should be able to list all emails, so we use NO_ACCESS
5
- # query ListEmails @auth(level: NO_ACCESS) {
6
- # emails {
7
- # id, subject, text, sent
8
- # from {
9
- # name
10
- # }
4
+ # # Anyone should be able to list all movies, so the auth level is set to PUBLIC
5
+ # query ListMovies @auth(level: PUBLIC) {
6
+ # movies {
7
+ # id
8
+ # title
9
+ # imageUrl
10
+ # genre
11
11
  # }
12
12
  # }
13
13
 
14
- # # Only admins should be able to list all users, so we use NO_ACCESS
14
+ # # List all users, only admins should be able to list all users, so we use NO_ACCESS
15
15
  # query ListUsers @auth(level: NO_ACCESS) {
16
- # users { uid, name, address }
16
+ # users { id, username }
17
17
  # }
18
-
19
- # # Logged in users should be able to see their inbox though, so we use USER
20
- # query ListInbox @auth(level: USER) {
21
- # # where allows you to filter lists
22
- # # Here, we use it to filter to only emails that are sent to the logged in user.
23
- # emails(where: {
24
- # users_via_Recipient: {
25
- # exist: { uid: { eq_expr: "auth.uid" }
26
- # }}
27
- # }) {
28
- # id subject sent
29
- # content: text # Select the `text` field but alias it as `content` in the response.
30
- # sender: from { name address uid }
18
+
19
+ # # Logged in user can list all their reviews and movie titles associated with the review
20
+ # # Since the query requires the uid of the current authenticated user, the auth level is set to USER
21
+ # query ListUserReviews @auth(level: USER) {
22
+ # user(key: {id_expr: "auth.uid"}) {
23
+ # id
24
+ # username
31
25
  # # <field>_on_<foreign_key_field> makes it easy to grab info from another table
32
- # # Here, we use it to grab all the recipients of the email.
33
- # to: recipients_on_email {
34
- # user { name address uid }
26
+ # # Here, we use it to grab all the reviews written by the user.
27
+ # reviews: reviews_on_user {
28
+ # id
29
+ # rating
30
+ # reviewDate
31
+ # reviewText
32
+ # movie {
33
+ # id
34
+ # title
35
+ # }
35
36
  # }
36
37
  # }
37
38
  # }
38
39
 
39
- # query GetUidByEmail($emails: [String!]) @auth(level: PUBLIC) {
40
- # users(where: { address: { in: $emails } }) {
41
- # uid address
40
+ # # Get movie by id
41
+ # query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
42
+ # movie(id: $id) {
43
+ # id
44
+ # title
45
+ # imageUrl
46
+ # genre
47
+ # metadata: movieMetadatas_on_movie {
48
+ # director
49
+ # rating
50
+ # releaseYear
51
+ # description
52
+ # }
53
+ # reviews: reviews_on_movie {
54
+ # id
55
+ # reviewText
56
+ # reviewDate
57
+ # rating
58
+ # user {
59
+ # id
60
+ # username
61
+ # }
62
+ # }
42
63
  # }
43
64
  # }
44
65
 
45
- # query ListSent($uid: String) @auth(level: PUBLIC) {
46
- # emails(where: {
47
- # from: {uid: {eq: $uid }}
48
- # }) {
49
- # id subject sent
50
- # content: text
51
- # sender: from { name address uid }
52
- # to: recipients_on_email {
53
- # user { name address uid }
66
+ # # Search for movies, actors, and reviews
67
+ # query SearchMovie(
68
+ # $titleInput: String
69
+ # $genre: String
70
+ # ) @auth(level: PUBLIC) {
71
+ # movies(
72
+ # where: {
73
+ # _and: [
74
+ # { genre: { eq: $genre } }
75
+ # { title: { contains: $titleInput } }
76
+ # ]
54
77
  # }
78
+ # ) {
79
+ # id
80
+ # title
81
+ # genre
82
+ # imageUrl
55
83
  # }
56
84
  # }