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.
- package/lib/bin/firebase.js +2 -4
- package/lib/commands/deploy.js +10 -2
- package/lib/commands/ext-configure.js +1 -6
- package/lib/commands/ext-dev-init.js +2 -4
- package/lib/commands/ext-dev-upload.js +4 -6
- package/lib/commands/ext-info.js +3 -5
- package/lib/commands/ext-install.js +0 -5
- package/lib/commands/ext-uninstall.js +0 -5
- package/lib/commands/ext-update.js +0 -5
- package/lib/commands/firestore-databases-create.js +19 -1
- package/lib/commands/functions-secrets-set.js +9 -6
- package/lib/commands/hosting-channel-create.js +1 -1
- package/lib/commands/hosting-channel-delete.js +1 -1
- package/lib/commands/hosting-channel-deploy.js +1 -1
- package/lib/commands/hosting-clone.js +1 -2
- package/lib/dataconnect/fileUtils.js +63 -1
- package/lib/dataconnect/provisionCloudSql.js +5 -1
- package/lib/dataconnect/types.js +8 -1
- package/lib/deploy/extensions/planner.js +46 -1
- package/lib/deploy/extensions/prepare.js +99 -23
- package/lib/deploy/functions/build.js +5 -5
- package/lib/deploy/functions/deploy.js +12 -12
- package/lib/deploy/functions/params.js +5 -3
- package/lib/deploy/functions/prepare.js +16 -1
- package/lib/deploy/functions/release/index.js +4 -0
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
- package/lib/emulator/dataconnectEmulator.js +2 -0
- package/lib/emulator/downloadableEmulators.js +11 -12
- package/lib/emulator/functionsEmulator.js +8 -1
- package/lib/extensions/askUserForEventsConfig.js +18 -8
- package/lib/extensions/askUserForParam.js +3 -2
- package/lib/extensions/change-log.js +2 -4
- package/lib/extensions/displayExtensionInfo.js +5 -10
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +47 -10
- package/lib/extensions/localHelper.js +1 -1
- package/lib/extensions/provisioningHelper.js +1 -1
- package/lib/extensions/refs.js +26 -11
- package/lib/extensions/runtimes/common.js +75 -0
- package/lib/extensions/types.js +56 -1
- package/lib/extensions/updateHelper.js +1 -2
- package/lib/extensions/warnings.js +2 -7
- package/lib/firestore/api.js +8 -7
- package/lib/firestore/pretty-print.js +21 -1
- package/lib/firestore/pretty-print.test.js +8 -0
- package/lib/frameworks/constants.js +1 -1
- package/lib/frameworks/next/constants.js +1 -1
- package/lib/frameworks/next/index.js +26 -14
- package/lib/frameworks/next/utils.js +45 -1
- package/lib/init/features/dataconnect/index.js +29 -15
- package/lib/init/features/dataconnect/sdk.js +72 -56
- package/lib/prompt.js +22 -1
- package/lib/rulesDeploy.js +14 -12
- package/package.json +3 -3
- package/templates/init/dataconnect/connector.yaml +5 -3
- package/templates/init/dataconnect/dataconnect.yaml +5 -5
- package/templates/init/dataconnect/mutations.gql +44 -29
- package/templates/init/dataconnect/queries.gql +66 -38
- package/templates/init/dataconnect/schema.gql +38 -21
- 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
|
|
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
|
-
|
|
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.
|
|
92
|
+
exports.actuate = actuate;
|
|
82
93
|
async function writeFiles(config, info) {
|
|
83
94
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
84
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
11
|
+
const types_1 = require("../../../dataconnect/types");
|
|
11
12
|
const dataconnectEmulator_1 = require("../../../emulator/dataconnectEmulator");
|
|
12
13
|
const error_1 = require("../../../error");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 (
|
|
67
|
-
const outputDir =
|
|
68
|
-
|
|
69
|
-
|
|
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 (
|
|
88
|
-
|
|
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 (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/lib/rulesDeploy.js
CHANGED
|
@@ -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 =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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 =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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.
|
|
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": "^
|
|
98
|
-
"marked-terminal": "^
|
|
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:
|
|
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
|
-
#
|
|
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:
|
|
3
|
-
location:
|
|
2
|
+
serviceId: __serviceId__
|
|
3
|
+
location: __location__
|
|
4
4
|
schema:
|
|
5
5
|
source: "./schema"
|
|
6
6
|
datasource:
|
|
7
7
|
postgresql:
|
|
8
|
-
database:
|
|
8
|
+
database: __cloudSqlDatabase__
|
|
9
9
|
cloudSql:
|
|
10
|
-
instanceId:
|
|
11
|
-
connectorDirs:
|
|
10
|
+
instanceId: __cloudSqlInstanceId__
|
|
11
|
+
connectorDirs: __connectorDirs__
|
|
@@ -1,35 +1,50 @@
|
|
|
1
|
-
# # Example mutations for a simple
|
|
1
|
+
# # Example mutations for a simple movie app
|
|
2
2
|
|
|
3
|
-
# #
|
|
4
|
-
# mutation
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
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
|
-
# #
|
|
16
|
-
# mutation
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
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
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
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
|
|
1
|
+
# # Example queries for a simple movie app.
|
|
2
2
|
|
|
3
3
|
# # @auth() directives control who can call each operation.
|
|
4
|
-
# #
|
|
5
|
-
# query
|
|
6
|
-
#
|
|
7
|
-
# id
|
|
8
|
-
#
|
|
9
|
-
#
|
|
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
|
-
# #
|
|
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 {
|
|
16
|
+
# users { id, username }
|
|
17
17
|
# }
|
|
18
|
-
|
|
19
|
-
# # Logged in
|
|
20
|
-
# query
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
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
|
|
33
|
-
#
|
|
34
|
-
#
|
|
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
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
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
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
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
|
# }
|