firebase-tools 13.20.2 → 13.22.0
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/apphosting/githubConnections.js +19 -1
- package/lib/apphosting/index.js +12 -46
- package/lib/apphosting/rollout.js +127 -0
- package/lib/command.js +10 -3
- package/lib/commands/apphosting-rollouts-create.js +13 -13
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/init.js +6 -0
- package/lib/config.js +14 -4
- package/lib/dataconnect/fileUtils.js +7 -43
- package/lib/dataconnect/freeTrial.js +44 -10
- package/lib/dataconnect/provisionCloudSql.js +4 -4
- package/lib/dataconnect/types.js +3 -2
- package/lib/dataconnect/webhook.js +2 -1
- package/lib/deploy/dataconnect/prepare.js +5 -0
- package/lib/emulator/apphosting/config.js +45 -0
- package/lib/emulator/apphosting/index.js +9 -11
- package/lib/emulator/apphosting/serve.js +16 -10
- package/lib/emulator/apphosting/utils.js +18 -0
- package/lib/emulator/controller.js +6 -1
- package/lib/emulator/downloadableEmulators.js +12 -12
- package/lib/emulator/eventarcEmulator.js +1 -1
- package/lib/emulator/extensionsEmulator.js +38 -6
- package/lib/emulator/functionsEmulator.js +85 -41
- package/lib/emulator/functionsEmulatorShared.js +2 -1
- package/lib/error.js +24 -1
- package/lib/extensions/emulator/triggerHelper.js +1 -1
- package/lib/extensions/extensionsApi.js +11 -8
- package/lib/extensions/runtimes/common.js +11 -19
- package/lib/extensions/runtimes/node.js +25 -22
- package/lib/extensions/types.js +16 -1
- package/lib/gcp/cloudmonitoring.js +3 -3
- package/lib/gcp/devConnect.js +38 -1
- package/lib/init/features/apphosting.js +12 -0
- package/lib/init/features/dataconnect/index.js +75 -85
- package/lib/init/features/dataconnect/sdk.js +69 -86
- package/lib/init/features/index.js +1 -1
- package/lib/init/index.js +1 -0
- package/lib/init/spawn.js +2 -1
- package/lib/requireAuth.js +9 -4
- package/package.json +1 -1
- package/templates/init/apphosting/apphosting.yaml +23 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.longestCommonPrefix = exports.snakeToCamelCase = exports.lowercaseFirstLetter = exports.capitalizeFirstLetter = exports.toTitleCase = exports.getInstallPathPrefix = exports.getCodebaseDir = exports.writeSDK = exports.getCodebaseRuntime = exports.copyDirectory = exports.writeFile = exports.isTypescriptCodebase = exports.extensionMatchesAnyFilter = exports.extractExtensionsFromBuilds = exports.
|
|
3
|
+
exports.longestCommonPrefix = exports.snakeToCamelCase = exports.lowercaseFirstLetter = exports.capitalizeFirstLetter = exports.toTitleCase = exports.getInstallPathPrefix = exports.getCodebaseDir = exports.writeSDK = exports.getCodebaseRuntime = exports.copyDirectory = exports.writeFile = exports.isTypescriptCodebase = exports.extensionMatchesAnyFilter = exports.extractExtensionsFromBuilds = exports.fixDarkBlueText = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const prompt_1 = require("../../prompt");
|
|
@@ -8,7 +8,6 @@ const fsutils = require("../../fsutils");
|
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
9
|
const error_1 = require("../../error");
|
|
10
10
|
const projectConfig_1 = require("../../functions/projectConfig");
|
|
11
|
-
const types_1 = require("../types");
|
|
12
11
|
const functionRuntimes = require("../../deploy/functions/runtimes");
|
|
13
12
|
const nodeRuntime = require("./node");
|
|
14
13
|
function fixDarkBlueText(txt) {
|
|
@@ -17,15 +16,6 @@ function fixDarkBlueText(txt) {
|
|
|
17
16
|
return txt.replaceAll(DARK_BLUE, BRIGHT_CYAN);
|
|
18
17
|
}
|
|
19
18
|
exports.fixDarkBlueText = fixDarkBlueText;
|
|
20
|
-
function getErrorMessage(err, defaultMsg) {
|
|
21
|
-
if ((0, types_1.isObject)(err) && err.message && typeof err.message === "string") {
|
|
22
|
-
return err.message;
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
return defaultMsg;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
exports.getErrorMessage = getErrorMessage;
|
|
29
19
|
function extractExtensionsFromBuilds(builds, filters) {
|
|
30
20
|
const extRecords = {};
|
|
31
21
|
for (const [codebase, build] of Object.entries(builds)) {
|
|
@@ -80,7 +70,7 @@ async function writeFile(filePath, data, options) {
|
|
|
80
70
|
(0, utils_1.logLabeledBullet)("extensions", `successfully wrote ${shortFilePath}`);
|
|
81
71
|
}
|
|
82
72
|
catch (err) {
|
|
83
|
-
throw new error_1.FirebaseError(`Failed to write ${shortFilePath}:\n ${err}`);
|
|
73
|
+
throw new error_1.FirebaseError(`Failed to write ${shortFilePath}:\n ${(0, error_1.getErrMsg)(err)}`);
|
|
84
74
|
}
|
|
85
75
|
}
|
|
86
76
|
else {
|
|
@@ -95,11 +85,11 @@ async function writeFile(filePath, data, options) {
|
|
|
95
85
|
(0, utils_1.logLabeledBullet)("extensions", `successfully created ${shortFilePath}`);
|
|
96
86
|
}
|
|
97
87
|
catch (err) {
|
|
98
|
-
throw new error_1.FirebaseError(`Failed to create ${shortFilePath}:\n ${err}`);
|
|
88
|
+
throw new error_1.FirebaseError(`Failed to create ${shortFilePath}:\n ${(0, error_1.getErrMsg)(err)}`);
|
|
99
89
|
}
|
|
100
90
|
}
|
|
101
91
|
catch (err) {
|
|
102
|
-
throw new error_1.FirebaseError(`Error during SDK file creation:\n ${err}`);
|
|
92
|
+
throw new error_1.FirebaseError(`Error during SDK file creation:\n ${(0, error_1.getErrMsg)(err)}`);
|
|
103
93
|
}
|
|
104
94
|
}
|
|
105
95
|
}
|
|
@@ -121,14 +111,14 @@ async function copyDirectory(src, dest, options) {
|
|
|
121
111
|
if (srcPath.includes("node_modules")) {
|
|
122
112
|
continue;
|
|
123
113
|
}
|
|
124
|
-
await copyDirectory(srcPath, destPath, { force: true });
|
|
114
|
+
await copyDirectory(srcPath, destPath, Object.assign(Object.assign({}, options), { force: true }));
|
|
125
115
|
}
|
|
126
116
|
else if (entry.isFile())
|
|
127
117
|
try {
|
|
128
118
|
await fs.promises.copyFile(srcPath, destPath);
|
|
129
119
|
}
|
|
130
120
|
catch (err) {
|
|
131
|
-
throw new error_1.FirebaseError(`Failed to copy ${destPath.replace(process.cwd(), ".")}:\n ${err}`);
|
|
121
|
+
throw new error_1.FirebaseError(`Failed to copy ${destPath.replace(process.cwd(), ".")}:\n ${(0, error_1.getErrMsg)(err)}`);
|
|
132
122
|
}
|
|
133
123
|
}
|
|
134
124
|
}
|
|
@@ -137,9 +127,8 @@ async function copyDirectory(src, dest, options) {
|
|
|
137
127
|
}
|
|
138
128
|
}
|
|
139
129
|
else {
|
|
140
|
-
await fs.promises.mkdir(dest, { recursive: true })
|
|
141
|
-
|
|
142
|
-
});
|
|
130
|
+
await fs.promises.mkdir(dest, { recursive: true });
|
|
131
|
+
await copyDirectory(src, dest, Object.assign(Object.assign({}, options), { force: true }));
|
|
143
132
|
}
|
|
144
133
|
}
|
|
145
134
|
exports.copyDirectory = copyDirectory;
|
|
@@ -177,6 +166,9 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
177
166
|
}
|
|
178
167
|
exports.writeSDK = writeSDK;
|
|
179
168
|
function getCodebaseDir(options) {
|
|
169
|
+
if (!options.projectRoot) {
|
|
170
|
+
throw new error_1.FirebaseError("Unable to determine root directory of project");
|
|
171
|
+
}
|
|
180
172
|
const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
181
173
|
const codebaseConfig = (0, projectConfig_1.configForCodebase)(config, options.codebase || projectConfig_1.DEFAULT_CODEBASE);
|
|
182
174
|
return `${options.projectRoot}/${codebaseConfig.source}/`;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.writeSDK = exports.TYPESCRIPT_VERSION = exports.FIREBASE_FUNCTIONS_VERSION = exports.SDK_GENERATION_VERSION = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const marked_terminal_1 = require("marked-terminal");
|
|
7
|
+
const marked_1 = require("marked");
|
|
4
8
|
const types_1 = require("../types");
|
|
5
9
|
const prompt_1 = require("../../prompt");
|
|
6
10
|
const secretsUtils = require("../secretsUtils");
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
11
|
const utils_1 = require("../../utils");
|
|
9
|
-
const path = require("path");
|
|
10
12
|
const common_1 = require("./common");
|
|
11
13
|
const askUserForEventsConfig_1 = require("../askUserForEventsConfig");
|
|
12
14
|
const extensionsHelper_1 = require("../extensionsHelper");
|
|
13
15
|
const error_1 = require("../../error");
|
|
14
|
-
const marked_terminal_1 = require("marked-terminal");
|
|
15
|
-
const marked_1 = require("marked");
|
|
16
16
|
marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
|
|
17
17
|
exports.SDK_GENERATION_VERSION = "1.0.0";
|
|
18
18
|
exports.FIREBASE_FUNCTIONS_VERSION = ">=5.1.0";
|
|
@@ -67,7 +67,7 @@ function makeClassName(name) {
|
|
|
67
67
|
function makeEventName(name, prefix) {
|
|
68
68
|
let eventName;
|
|
69
69
|
const versionedEvent = /^(?:[^.]+[.])+(?:[vV]\d+[.])(?<event>.*)$/;
|
|
70
|
-
const match =
|
|
70
|
+
const match = versionedEvent.exec(name);
|
|
71
71
|
if (match) {
|
|
72
72
|
eventName = match[1];
|
|
73
73
|
}
|
|
@@ -79,7 +79,7 @@ function makeEventName(name, prefix) {
|
|
|
79
79
|
eventName = parts[parts.length - 1];
|
|
80
80
|
}
|
|
81
81
|
const allCaps = /^[A-Z._-]+$/;
|
|
82
|
-
eventName =
|
|
82
|
+
eventName = allCaps.exec(eventName) ? eventName : eventName.replace(/([A-Z])/g, " $1").trim();
|
|
83
83
|
eventName = eventName.replace(/[._-]/g, " ");
|
|
84
84
|
eventName = eventName.toLowerCase().startsWith("on") ? eventName : "on " + eventName;
|
|
85
85
|
eventName = eventName.replace(/\w\S*/g, common_1.toTitleCase);
|
|
@@ -87,6 +87,15 @@ function makeEventName(name, prefix) {
|
|
|
87
87
|
eventName = eventName.charAt(0).toLowerCase() + eventName.substring(1);
|
|
88
88
|
return eventName;
|
|
89
89
|
}
|
|
90
|
+
function addPeerDependency(pkgJson, dependency, version) {
|
|
91
|
+
if (!pkgJson.peerDependencies) {
|
|
92
|
+
pkgJson.peerDependencies = {};
|
|
93
|
+
}
|
|
94
|
+
if (!(0, types_1.isObject)(pkgJson.peerDependencies)) {
|
|
95
|
+
throw new error_1.FirebaseError("Internal error generating peer dependencies.");
|
|
96
|
+
}
|
|
97
|
+
pkgJson.peerDependencies[dependency] = version;
|
|
98
|
+
}
|
|
90
99
|
async function writeSDK(extensionRef, localPath, spec, options) {
|
|
91
100
|
var _a, _b, _c;
|
|
92
101
|
const sdkLines = [];
|
|
@@ -105,7 +114,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
105
114
|
})) {
|
|
106
115
|
const newLocalPath = path.join(dirPath, "src");
|
|
107
116
|
await (0, common_1.copyDirectory)(localPath, newLocalPath, options);
|
|
108
|
-
localPath = newLocalPath.replace(options.projectRoot, ".");
|
|
117
|
+
localPath = newLocalPath.replace(options.projectRoot || ".", ".");
|
|
109
118
|
}
|
|
110
119
|
}
|
|
111
120
|
if (!dirPath) {
|
|
@@ -115,7 +124,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
115
124
|
const pkgJson = {
|
|
116
125
|
name: packageName,
|
|
117
126
|
version: `${exports.SDK_GENERATION_VERSION}`,
|
|
118
|
-
description: `Generated SDK for ${spec.displayName}@${spec.version}`,
|
|
127
|
+
description: `Generated SDK for ${spec.displayName || spec.name}@${spec.version}`,
|
|
119
128
|
main: "./output/index.js",
|
|
120
129
|
private: true,
|
|
121
130
|
scripts: {
|
|
@@ -138,7 +147,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
138
147
|
},
|
|
139
148
|
};
|
|
140
149
|
sdkLines.push("/**");
|
|
141
|
-
sdkLines.push(` * ${spec.displayName} SDK for ${spec.name}@${spec.version}`);
|
|
150
|
+
sdkLines.push(` * ${spec.displayName || spec.name} SDK for ${spec.name}@${spec.version}`);
|
|
142
151
|
sdkLines.push(" *");
|
|
143
152
|
sdkLines.push(" * When filing bugs or feature requests please specify:");
|
|
144
153
|
if (extensionRef) {
|
|
@@ -155,18 +164,12 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
155
164
|
if (hasEvents) {
|
|
156
165
|
sdkLines.push(`import { CloudEvent } from "firebase-functions/v2";`);
|
|
157
166
|
sdkLines.push(`import { onCustomEventPublished, EventarcTriggerOptions } from "firebase-functions/v2/eventarc";`);
|
|
158
|
-
|
|
159
|
-
pkgJson.peerDependencies = {};
|
|
160
|
-
}
|
|
161
|
-
pkgJson.peerDependencies["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION;
|
|
167
|
+
addPeerDependency(pkgJson, "firebase-functions", exports.FIREBASE_FUNCTIONS_VERSION);
|
|
162
168
|
}
|
|
163
169
|
const usesSecrets = secretsUtils.usesSecrets(spec);
|
|
164
170
|
if (usesSecrets) {
|
|
165
171
|
sdkLines.push(`import { defineSecret } from "firebase-functions/params";`);
|
|
166
|
-
|
|
167
|
-
pkgJson.peerDependencies = {};
|
|
168
|
-
}
|
|
169
|
-
pkgJson.peerDependencies["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION;
|
|
172
|
+
addPeerDependency(pkgJson, "firebase-functions", exports.FIREBASE_FUNCTIONS_VERSION);
|
|
170
173
|
}
|
|
171
174
|
if (hasEvents || usesSecrets) {
|
|
172
175
|
sdkLines.push("");
|
|
@@ -289,7 +292,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
289
292
|
sdkLines.push(` ${sysParam.param}${opt}: string;`);
|
|
290
293
|
break;
|
|
291
294
|
default:
|
|
292
|
-
throw new error_1.FirebaseError(`Error: Unknown systemParam type: ${sysParam.type}.`);
|
|
295
|
+
throw new error_1.FirebaseError(`Error: Unknown systemParam type: ${sysParam.type || "undefined"}.`);
|
|
293
296
|
}
|
|
294
297
|
sdkLines.push("");
|
|
295
298
|
}
|
|
@@ -299,7 +302,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
299
302
|
sdkLines.push(` return new ${className}(instanceId, params);`);
|
|
300
303
|
sdkLines.push("}\n");
|
|
301
304
|
sdkLines.push(`/**`);
|
|
302
|
-
sdkLines.push(` * ${spec.displayName}`);
|
|
305
|
+
sdkLines.push(` * ${spec.displayName || spec.name}`);
|
|
303
306
|
(_c = spec.description) === null || _c === void 0 ? void 0 : _c.split("\n").forEach((val) => {
|
|
304
307
|
sdkLines.push(` * ${val.replace(/\*\//g, "* /")}`);
|
|
305
308
|
});
|
|
@@ -347,7 +350,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
347
350
|
(0, child_process_1.execFileSync)("npm", ["--prefix", dirPath, "install"]);
|
|
348
351
|
}
|
|
349
352
|
catch (err) {
|
|
350
|
-
const errMsg = (0,
|
|
353
|
+
const errMsg = (0, error_1.getErrMsg)(err, "unknown error");
|
|
351
354
|
throw new error_1.FirebaseError(`Error during npm install in ${shortDirPath}: ${errMsg}`);
|
|
352
355
|
}
|
|
353
356
|
(0, utils_1.logLabeledBullet)("extensions", `running 'npm --prefix ${shortDirPath} run build'`);
|
|
@@ -355,7 +358,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
355
358
|
(0, child_process_1.execFileSync)("npm", ["--prefix", dirPath, "run", "build"]);
|
|
356
359
|
}
|
|
357
360
|
catch (err) {
|
|
358
|
-
const errMsg = (0,
|
|
361
|
+
const errMsg = (0, error_1.getErrMsg)(err, "unknown error");
|
|
359
362
|
throw new error_1.FirebaseError(`Error during npm run build in ${shortDirPath}: ${errMsg}`);
|
|
360
363
|
}
|
|
361
364
|
const codebaseDir = (0, common_1.getCodebaseDir)(options);
|
|
@@ -372,7 +375,7 @@ async function writeSDK(extensionRef, localPath, spec, options) {
|
|
|
372
375
|
(0, child_process_1.execFileSync)("npm", ["--prefix", codebaseDir, "install", "--save", dirPath]);
|
|
373
376
|
}
|
|
374
377
|
catch (err) {
|
|
375
|
-
const errMsg = (0,
|
|
378
|
+
const errMsg = (0, error_1.getErrMsg)(err, "unknown error");
|
|
376
379
|
throw new error_1.FirebaseError(`Error during npm install in ${codebaseDir}: ${errMsg}`);
|
|
377
380
|
}
|
|
378
381
|
}
|
package/lib/extensions/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isExtensionSpec = exports.isResource = exports.isParam = exports.isObject = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
3
|
+
exports.isExtensionSpec = exports.isResource = exports.isParam = exports.isObject = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.isExtensionInstance = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
4
4
|
var RegistryLaunchStage;
|
|
5
5
|
(function (RegistryLaunchStage) {
|
|
6
6
|
RegistryLaunchStage["EXPERIMENTAL"] = "EXPERIMENTAL";
|
|
@@ -14,6 +14,21 @@ var Visibility;
|
|
|
14
14
|
Visibility["UNLISTED"] = "unlisted";
|
|
15
15
|
Visibility["PUBLIC"] = "public";
|
|
16
16
|
})(Visibility = exports.Visibility || (exports.Visibility = {}));
|
|
17
|
+
const extensionInstanceState = [
|
|
18
|
+
"STATE_UNSPECIFIED",
|
|
19
|
+
"DEPLOYING",
|
|
20
|
+
"UNINSTALLING",
|
|
21
|
+
"ACTIVE",
|
|
22
|
+
"ERRORED",
|
|
23
|
+
"PAUSED",
|
|
24
|
+
];
|
|
25
|
+
const isExtensionInstance = (value) => {
|
|
26
|
+
if (!isObject(value) || typeof value.name !== "string") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
exports.isExtensionInstance = isExtensionInstance;
|
|
17
32
|
const lifecycleStages = ["STAGE_UNSPECIFIED", "ON_INSTALL", "ON_UPDATE", "ON_CONFIGURE"];
|
|
18
33
|
exports.FUNCTIONS_RESOURCE_TYPE = "firebaseextensions.v1beta.function";
|
|
19
34
|
exports.FUNCTIONS_V2_RESOURCE_TYPE = "firebaseextensions.v1beta.v2function";
|
|
@@ -57,19 +57,19 @@ var ValueType;
|
|
|
57
57
|
ValueType["DOUBLE"] = "DOUBLE";
|
|
58
58
|
ValueType["STRING"] = "STRING";
|
|
59
59
|
})(ValueType = exports.ValueType || (exports.ValueType = {}));
|
|
60
|
-
async function queryTimeSeries(query,
|
|
60
|
+
async function queryTimeSeries(query, project) {
|
|
61
61
|
const client = new apiv2_1.Client({
|
|
62
62
|
urlPrefix: (0, api_1.cloudMonitoringOrigin)(),
|
|
63
63
|
apiVersion: exports.CLOUD_MONITORING_VERSION,
|
|
64
64
|
});
|
|
65
65
|
try {
|
|
66
|
-
const res = await client.get(`/projects/${
|
|
66
|
+
const res = await client.get(`/projects/${project}/timeSeries/`, {
|
|
67
67
|
queryParams: query,
|
|
68
68
|
});
|
|
69
69
|
return res.body.timeSeries;
|
|
70
70
|
}
|
|
71
71
|
catch (err) {
|
|
72
|
-
throw new error_1.FirebaseError(`Failed to get
|
|
72
|
+
throw new error_1.FirebaseError(`Failed to get Cloud Monitoring metric: ${err}`, {
|
|
73
73
|
status: err.status,
|
|
74
74
|
});
|
|
75
75
|
}
|
package/lib/gcp/devConnect.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateP4SA = exports.serviceAgentEmail = exports.sortConnectionsByCreateTime = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.fetchGitHubInstallations = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
|
|
3
|
+
exports.getRepoDetailsFromBackend = exports.extractGitRepositoryLinkComponents = exports.generateP4SA = exports.serviceAgentEmail = exports.sortConnectionsByCreateTime = exports.fetchGitRepositoryLinkReadToken = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.fetchGitHubInstallations = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
|
|
4
4
|
const apiv2_1 = require("../apiv2");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const serviceusage_1 = require("./serviceusage");
|
|
7
|
+
const error_1 = require("../error");
|
|
8
|
+
const githubConnections_1 = require("../apphosting/githubConnections");
|
|
7
9
|
const PAGE_SIZE_MAX = 1000;
|
|
8
10
|
const LOCATION_OVERRIDE = process.env.FIREBASE_DEVELOPERCONNECT_LOCATION_OVERRIDE;
|
|
9
11
|
exports.client = new apiv2_1.Client({
|
|
@@ -112,6 +114,12 @@ async function getGitRepositoryLink(projectId, location, connectionId, gitReposi
|
|
|
112
114
|
return res.body;
|
|
113
115
|
}
|
|
114
116
|
exports.getGitRepositoryLink = getGitRepositoryLink;
|
|
117
|
+
async function fetchGitRepositoryLinkReadToken(projectId, location, connectionId, gitRepositoryLinkId) {
|
|
118
|
+
const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}/gitRepositoryLinks/${gitRepositoryLinkId}:fetchReadToken`;
|
|
119
|
+
const res = await exports.client.post(name);
|
|
120
|
+
return res.body;
|
|
121
|
+
}
|
|
122
|
+
exports.fetchGitRepositoryLinkReadToken = fetchGitRepositoryLinkReadToken;
|
|
115
123
|
function sortConnectionsByCreateTime(connections) {
|
|
116
124
|
return connections.sort((a, b) => {
|
|
117
125
|
return Date.parse(a.createTime) - Date.parse(b.createTime);
|
|
@@ -127,3 +135,32 @@ async function generateP4SA(projectNumber) {
|
|
|
127
135
|
await (0, serviceusage_1.generateServiceIdentityAndPoll)(projectNumber, new URL(devConnectOrigin).hostname, "apphosting");
|
|
128
136
|
}
|
|
129
137
|
exports.generateP4SA = generateP4SA;
|
|
138
|
+
function extractGitRepositoryLinkComponents(path) {
|
|
139
|
+
const connectionMatch = /connections\/([^\/]+)/.exec(path);
|
|
140
|
+
const repositoryMatch = /gitRepositoryLinks\/([^\/]+)/.exec(path);
|
|
141
|
+
const connection = connectionMatch ? connectionMatch[1] : null;
|
|
142
|
+
const gitRepoLink = repositoryMatch ? repositoryMatch[1] : null;
|
|
143
|
+
return { connection, gitRepoLink };
|
|
144
|
+
}
|
|
145
|
+
exports.extractGitRepositoryLinkComponents = extractGitRepositoryLinkComponents;
|
|
146
|
+
async function getRepoDetailsFromBackend(projectId, location, gitRepoLinkPath) {
|
|
147
|
+
const { connection, gitRepoLink } = extractGitRepositoryLinkComponents(gitRepoLinkPath);
|
|
148
|
+
if (!connection || !gitRepoLink) {
|
|
149
|
+
throw new error_1.FirebaseError(`Failed to extract connection or repository resource names from backend repository name.`);
|
|
150
|
+
}
|
|
151
|
+
const repoLink = await getGitRepositoryLink(projectId, location, connection, gitRepoLink);
|
|
152
|
+
const repoSlug = (0, githubConnections_1.extractRepoSlugFromUri)(repoLink.cloneUri);
|
|
153
|
+
const owner = repoSlug === null || repoSlug === void 0 ? void 0 : repoSlug.split("/")[0];
|
|
154
|
+
const repo = repoSlug === null || repoSlug === void 0 ? void 0 : repoSlug.split("/")[1];
|
|
155
|
+
if (!owner || !repo) {
|
|
156
|
+
throw new error_1.FirebaseError("Failed to parse owner and repo from git repository link");
|
|
157
|
+
}
|
|
158
|
+
const readToken = await fetchGitRepositoryLinkReadToken(projectId, location, connection, gitRepoLink);
|
|
159
|
+
return {
|
|
160
|
+
repoLink,
|
|
161
|
+
owner,
|
|
162
|
+
repo,
|
|
163
|
+
readToken,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
exports.getRepoDetailsFromBackend = getRepoDetailsFromBackend;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.doSetup = void 0;
|
|
4
|
+
const clc = require("colorette");
|
|
5
|
+
const utils = require("../../utils");
|
|
6
|
+
const templates_1 = require("../../templates");
|
|
7
|
+
const APPHOSTING_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/apphosting/apphosting.yaml");
|
|
8
|
+
async function doSetup(setup, config) {
|
|
9
|
+
utils.logBullet("Writing default settings to " + clc.bold("apphosting.yaml") + "...");
|
|
10
|
+
await config.askWriteProjectFile("apphosting.yaml", APPHOSTING_YAML_TEMPLATE);
|
|
11
|
+
}
|
|
12
|
+
exports.doSetup = doSetup;
|
|
@@ -24,6 +24,11 @@ const CONNECTOR_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconn
|
|
|
24
24
|
const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/schema.gql");
|
|
25
25
|
const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/queries.gql");
|
|
26
26
|
const MUTATIONS_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/mutations.gql");
|
|
27
|
+
const emptyConnector = {
|
|
28
|
+
id: "default",
|
|
29
|
+
path: "./connector",
|
|
30
|
+
files: [],
|
|
31
|
+
};
|
|
27
32
|
const defaultConnector = {
|
|
28
33
|
id: "default",
|
|
29
34
|
path: "./connector",
|
|
@@ -38,29 +43,27 @@ const defaultConnector = {
|
|
|
38
43
|
},
|
|
39
44
|
],
|
|
40
45
|
};
|
|
46
|
+
const defaultSchema = { path: "schema.gql", content: SCHEMA_TEMPLATE };
|
|
41
47
|
async function doSetup(setup, config) {
|
|
42
|
-
const
|
|
48
|
+
const isBillingEnabled = setup.projectId ? await (0, cloudbilling_1.checkBillingEnabled)(setup.projectId) : false;
|
|
49
|
+
if (setup.projectId) {
|
|
50
|
+
isBillingEnabled ? await (0, ensureApis_1.ensureApis)(setup.projectId) : await (0, ensureApis_1.ensureSparkApis)(setup.projectId);
|
|
51
|
+
}
|
|
52
|
+
const info = await askQuestions(setup, isBillingEnabled);
|
|
43
53
|
await actuate(setup, config, info);
|
|
44
54
|
const cwdPlatformGuess = await (0, fileUtils_1.getPlatformFromFolder)(process.cwd());
|
|
45
|
-
if (cwdPlatformGuess !== types_1.Platform.
|
|
55
|
+
if (cwdPlatformGuess !== types_1.Platform.NONE) {
|
|
46
56
|
await sdk.doSetup(setup, config);
|
|
47
57
|
}
|
|
48
58
|
else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (promptForSDKGeneration) {
|
|
54
|
-
await sdk.doSetup(setup, config);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
(0, utils_1.logBullet)(`If you'd like to generate an SDK for your new connector later, run ${clc.bold("firebase init dataconnect:sdk")}`);
|
|
58
|
-
}
|
|
59
|
+
(0, utils_1.logBullet)(`If you'd like to add the generated SDK to your app your, run ${clc.bold("firebase init dataconnect:sdk")}`);
|
|
60
|
+
}
|
|
61
|
+
if (setup.projectId && !isBillingEnabled) {
|
|
62
|
+
(0, utils_1.logBullet)((0, freeTrial_1.upgradeInstructions)(setup.projectId));
|
|
59
63
|
}
|
|
60
|
-
logger_1.logger.info("");
|
|
61
64
|
}
|
|
62
65
|
exports.doSetup = doSetup;
|
|
63
|
-
async function askQuestions(setup) {
|
|
66
|
+
async function askQuestions(setup, isBillingEnabled) {
|
|
64
67
|
let info = {
|
|
65
68
|
serviceId: "",
|
|
66
69
|
locationId: "",
|
|
@@ -69,14 +72,10 @@ async function askQuestions(setup) {
|
|
|
69
72
|
cloudSqlDatabase: "",
|
|
70
73
|
isNewDatabase: false,
|
|
71
74
|
connectors: [defaultConnector],
|
|
72
|
-
schemaGql: [],
|
|
75
|
+
schemaGql: [defaultSchema],
|
|
73
76
|
shouldProvisionCSQL: false,
|
|
74
77
|
};
|
|
75
|
-
|
|
76
|
-
if (setup.projectId) {
|
|
77
|
-
isBillingEnabled ? await (0, ensureApis_1.ensureApis)(setup.projectId) : await (0, ensureApis_1.ensureSparkApis)(setup.projectId);
|
|
78
|
-
}
|
|
79
|
-
info = await checkExistingInstances(setup, info, isBillingEnabled);
|
|
78
|
+
info = await promptForExistingServices(setup, info, isBillingEnabled);
|
|
80
79
|
const requiredConfigUnset = info.serviceId === "" ||
|
|
81
80
|
info.cloudSqlInstanceId === "" ||
|
|
82
81
|
info.locationId === "" ||
|
|
@@ -84,13 +83,12 @@ async function askQuestions(setup) {
|
|
|
84
83
|
const shouldConfigureBackend = isBillingEnabled && requiredConfigUnset
|
|
85
84
|
? await (0, prompt_1.confirm)({
|
|
86
85
|
message: `Would you like to configure your backend resources now?`,
|
|
87
|
-
default:
|
|
86
|
+
default: true,
|
|
88
87
|
})
|
|
89
88
|
: false;
|
|
90
89
|
if (shouldConfigureBackend) {
|
|
91
90
|
info = await promptForService(info);
|
|
92
|
-
info = await
|
|
93
|
-
info = await promptForDatabase(info);
|
|
91
|
+
info = await promptForCloudSQL(setup, info);
|
|
94
92
|
info.shouldProvisionCSQL = !!(setup.projectId &&
|
|
95
93
|
(info.isNewInstance || info.isNewDatabase) &&
|
|
96
94
|
isBillingEnabled &&
|
|
@@ -100,14 +98,10 @@ async function askQuestions(setup) {
|
|
|
100
98
|
})));
|
|
101
99
|
}
|
|
102
100
|
else {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
info.
|
|
107
|
-
info.cloudSqlInstanceId =
|
|
108
|
-
info.cloudSqlInstanceId !== "" ? info.cloudSqlInstanceId : `${info.serviceId || "app"}-fdc`;
|
|
109
|
-
info.locationId = info.locationId !== "" ? info.locationId : `us-central1`;
|
|
110
|
-
info.cloudSqlDatabase = info.cloudSqlDatabase !== "" ? info.cloudSqlDatabase : `fdcdb`;
|
|
101
|
+
info.serviceId = info.serviceId || (0, path_1.basename)(process.cwd());
|
|
102
|
+
info.cloudSqlInstanceId = info.cloudSqlInstanceId || `${info.serviceId || "app"}-fdc`;
|
|
103
|
+
info.locationId = info.locationId || `us-central1`;
|
|
104
|
+
info.cloudSqlDatabase = info.cloudSqlDatabase || `fdcdb`;
|
|
111
105
|
}
|
|
112
106
|
return info;
|
|
113
107
|
}
|
|
@@ -129,17 +123,19 @@ exports.actuate = actuate;
|
|
|
129
123
|
async function writeFiles(config, info) {
|
|
130
124
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
131
125
|
const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: info.connectors.map((c) => c.path) }));
|
|
126
|
+
if (!config.get("dataconnect.source")) {
|
|
127
|
+
if (!info.schemaGql.length && !info.connectors.flatMap((r) => r.files).length) {
|
|
128
|
+
info.schemaGql = [defaultSchema];
|
|
129
|
+
info.connectors = [defaultConnector];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
132
|
config.set("dataconnect", { source: dir });
|
|
133
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
|
|
133
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml, false, true);
|
|
134
134
|
if (info.schemaGql.length) {
|
|
135
|
-
(0, utils_1.logSuccess)("The service you chose already has GQL files deployed. We'll use those instead of the default templates.");
|
|
136
135
|
for (const f of info.schemaGql) {
|
|
137
136
|
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", f.path), f.content);
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
|
-
else {
|
|
141
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
|
|
142
|
-
}
|
|
143
139
|
for (const c of info.connectors) {
|
|
144
140
|
await writeConnectorFiles(config, c);
|
|
145
141
|
}
|
|
@@ -178,8 +174,8 @@ function subConnectorYamlValues(replacementValues) {
|
|
|
178
174
|
}
|
|
179
175
|
return replaced;
|
|
180
176
|
}
|
|
181
|
-
async function
|
|
182
|
-
var _a, _b, _c;
|
|
177
|
+
async function promptForExistingServices(setup, info, isBillingEnabled) {
|
|
178
|
+
var _a, _b, _c, _d;
|
|
183
179
|
if (!setup.projectId || !isBillingEnabled) {
|
|
184
180
|
return info;
|
|
185
181
|
}
|
|
@@ -208,16 +204,18 @@ async function checkExistingInstances(setup, info, isBillingEnabled) {
|
|
|
208
204
|
const serviceName = (0, names_1.parseServiceName)(choice.service.name);
|
|
209
205
|
info.serviceId = serviceName.serviceId;
|
|
210
206
|
info.locationId = serviceName.location;
|
|
207
|
+
info.schemaGql = [];
|
|
208
|
+
info.connectors = [emptyConnector];
|
|
211
209
|
if (choice.schema) {
|
|
212
210
|
const primaryDatasource = choice.schema.datasources.find((d) => d.postgresql);
|
|
213
211
|
if ((_a = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) {
|
|
214
212
|
const instanceName = (0, names_1.parseCloudSQLInstanceName)(primaryDatasource.postgresql.cloudSql.instance);
|
|
215
213
|
info.cloudSqlInstanceId = instanceName.instanceId;
|
|
216
214
|
}
|
|
217
|
-
if (choice.schema.source.files) {
|
|
215
|
+
if ((_b = choice.schema.source.files) === null || _b === void 0 ? void 0 : _b.length) {
|
|
218
216
|
info.schemaGql = choice.schema.source.files;
|
|
219
217
|
}
|
|
220
|
-
info.cloudSqlDatabase = (
|
|
218
|
+
info.cloudSqlDatabase = (_d = (_c = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
|
|
221
219
|
const connectors = await (0, client_1.listConnectors)(choice.service.name, [
|
|
222
220
|
"connectors.name",
|
|
223
221
|
"connectors.source.files",
|
|
@@ -234,20 +232,24 @@ async function checkExistingInstances(setup, info, isBillingEnabled) {
|
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
|
-
else {
|
|
238
|
-
info = await promptForService(info);
|
|
239
|
-
}
|
|
240
235
|
}
|
|
241
|
-
|
|
236
|
+
return info;
|
|
237
|
+
}
|
|
238
|
+
async function promptForCloudSQL(setup, info) {
|
|
239
|
+
if (info.cloudSqlInstanceId === "" && setup.projectId) {
|
|
242
240
|
const instances = await cloudsql.listInstances(setup.projectId);
|
|
243
241
|
let choices = instances.map((i) => {
|
|
244
|
-
|
|
242
|
+
var _a;
|
|
243
|
+
let display = `${i.name} (${i.region})`;
|
|
244
|
+
if (((_a = i.settings.userLabels) === null || _a === void 0 ? void 0 : _a["firebase-data-connect"]) === "ft") {
|
|
245
|
+
display += " (no cost trial)";
|
|
246
|
+
}
|
|
247
|
+
return { name: display, value: i.name, location: i.region };
|
|
245
248
|
});
|
|
246
249
|
choices = choices.filter((c) => info.locationId === "" || info.locationId === c.location);
|
|
247
250
|
if (choices.length) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
choices.push({ name: "Create a new instance", value: "", location: "" });
|
|
251
|
+
if (!(await (0, freeTrial_1.checkFreeTrialInstanceUsed)(setup.projectId))) {
|
|
252
|
+
choices.push({ name: "Create a new free trial instance", value: "", location: "" });
|
|
251
253
|
}
|
|
252
254
|
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
253
255
|
message: `Which CloudSQL instance would you like to use?`,
|
|
@@ -257,12 +259,25 @@ async function checkExistingInstances(setup, info, isBillingEnabled) {
|
|
|
257
259
|
if (info.cloudSqlInstanceId !== "") {
|
|
258
260
|
info.locationId = choices.find((c) => c.value === info.cloudSqlInstanceId).location;
|
|
259
261
|
}
|
|
260
|
-
else {
|
|
261
|
-
info = await promptForCloudSQLInstance(setup, info);
|
|
262
|
-
}
|
|
263
262
|
}
|
|
264
263
|
}
|
|
265
|
-
if (info.
|
|
264
|
+
if (info.cloudSqlInstanceId === "") {
|
|
265
|
+
info.isNewInstance = true;
|
|
266
|
+
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
267
|
+
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
268
|
+
type: "input",
|
|
269
|
+
default: `${info.serviceId || "app"}-fdc`,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
if (info.locationId === "") {
|
|
273
|
+
const choices = await locationChoices(setup);
|
|
274
|
+
info.locationId = await (0, prompt_1.promptOnce)({
|
|
275
|
+
message: "What location would like to use?",
|
|
276
|
+
type: "list",
|
|
277
|
+
choices,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (info.cloudSqlDatabase === "" && setup.projectId) {
|
|
266
281
|
try {
|
|
267
282
|
const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
|
|
268
283
|
const choices = dbs.map((d) => {
|
|
@@ -275,15 +290,20 @@ async function checkExistingInstances(setup, info, isBillingEnabled) {
|
|
|
275
290
|
type: "list",
|
|
276
291
|
choices,
|
|
277
292
|
});
|
|
278
|
-
if (info.cloudSqlDatabase === "") {
|
|
279
|
-
info = await promptForDatabase(info);
|
|
280
|
-
}
|
|
281
293
|
}
|
|
282
294
|
}
|
|
283
295
|
catch (err) {
|
|
284
296
|
logger_1.logger.debug(`[dataconnect] Cannot list databases during init: ${err}`);
|
|
285
297
|
}
|
|
286
298
|
}
|
|
299
|
+
if (info.cloudSqlDatabase === "") {
|
|
300
|
+
info.isNewDatabase = true;
|
|
301
|
+
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
302
|
+
message: `What ID would you like to use for your new database in ${info.cloudSqlInstanceId}?`,
|
|
303
|
+
type: "input",
|
|
304
|
+
default: `fdcdb`,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
287
307
|
return info;
|
|
288
308
|
}
|
|
289
309
|
async function promptForService(info) {
|
|
@@ -296,25 +316,6 @@ async function promptForService(info) {
|
|
|
296
316
|
}
|
|
297
317
|
return info;
|
|
298
318
|
}
|
|
299
|
-
async function promptForCloudSQLInstance(setup, info) {
|
|
300
|
-
if (info.cloudSqlInstanceId === "") {
|
|
301
|
-
info.isNewInstance = true;
|
|
302
|
-
info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
|
|
303
|
-
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
304
|
-
type: "input",
|
|
305
|
-
default: `${info.serviceId || "app"}-fdc`,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
if (info.locationId === "") {
|
|
309
|
-
const choices = await locationChoices(setup);
|
|
310
|
-
info.locationId = await (0, prompt_1.promptOnce)({
|
|
311
|
-
message: "What location would like to use?",
|
|
312
|
-
type: "list",
|
|
313
|
-
choices,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
return info;
|
|
317
|
-
}
|
|
318
319
|
async function locationChoices(setup) {
|
|
319
320
|
if (setup.projectId) {
|
|
320
321
|
const locations = await (0, client_1.listLocations)(setup.projectId);
|
|
@@ -335,14 +336,3 @@ async function locationChoices(setup) {
|
|
|
335
336
|
];
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
|
-
async function promptForDatabase(info) {
|
|
339
|
-
if (info.cloudSqlDatabase === "") {
|
|
340
|
-
info.isNewDatabase = true;
|
|
341
|
-
info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
|
|
342
|
-
message: `What ID would you like to use for your new database in ${info.cloudSqlInstanceId}?`,
|
|
343
|
-
type: "input",
|
|
344
|
-
default: `fdcdb`,
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
return info;
|
|
348
|
-
}
|