firebase-tools 15.5.1 → 15.7.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/api.js +3 -1
- package/lib/apiv2.js +6 -6
- package/lib/bin/mcp.js +1 -1
- package/lib/commands/deploy.js +3 -0
- package/lib/commands/init.js +5 -0
- package/lib/config.js +1 -0
- package/lib/dataconnect/client.js +2 -2
- package/lib/dataconnect/prompts.js +14 -0
- package/lib/deploy/auth/deploy.js +63 -0
- package/lib/deploy/auth/index.js +9 -0
- package/lib/deploy/auth/prepare.js +26 -0
- package/lib/deploy/auth/release.js +5 -0
- package/lib/deploy/dataconnect/release.js +11 -0
- package/lib/deploy/functions/backend.js +10 -0
- package/lib/deploy/functions/build.js +2 -2
- package/lib/deploy/functions/checkIam.js +3 -2
- package/lib/deploy/functions/deploy.js +4 -2
- package/lib/deploy/functions/prepare.js +5 -2
- package/lib/deploy/functions/prepareFunctionsUpload.js +7 -5
- package/lib/deploy/functions/release/fabricator.js +96 -15
- package/lib/deploy/functions/release/index.js +2 -1
- package/lib/deploy/functions/runtimes/dart.js +42 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +4 -1
- package/lib/deploy/functions/runtimes/index.js +9 -1
- package/lib/deploy/functions/runtimes/supported/types.js +6 -0
- package/lib/deploy/index.js +2 -0
- package/lib/emulator/auth/index.js +23 -17
- package/lib/emulator/auth/operations.js +9 -1
- package/lib/emulator/downloadableEmulatorInfo.json +31 -31
- package/lib/emulator/hubExport.js +15 -1
- package/lib/gcp/cloudbilling.js +6 -3
- package/lib/gcp/runv2.js +75 -3
- package/lib/gemini/fdcExperience.js +0 -11
- package/lib/init/features/auth.js +72 -0
- package/lib/init/features/dataconnect/resolver.js +17 -3
- package/lib/init/features/functions/index.js +22 -14
- package/lib/init/features/functions/javascript.js +18 -3
- package/lib/init/features/functions/typescript.js +20 -3
- package/lib/init/features/functions/utils.js +10 -0
- package/lib/init/features/genkit/index.js +3 -2
- package/lib/init/features/index.js +6 -2
- package/lib/init/index.js +11 -1
- package/lib/management/provisioning/provision.js +3 -0
- package/lib/management/provisioning/types.js +7 -0
- package/lib/mcp/onemcp/index.js +8 -0
- package/lib/mcp/onemcp/onemcp_server.js +77 -0
- package/lib/mcp/prompts/index.js +1 -0
- package/lib/mcp/resources/guides/init_auth.js +19 -2
- package/lib/mcp/tools/apptesting/tests.js +6 -4
- package/lib/mcp/tools/core/init.js +31 -0
- package/lib/mcp/tools/index.js +52 -32
- package/lib/mcp/types.js +1 -0
- package/lib/mcp/util/availability.js +2 -1
- package/lib/mcp/util.js +2 -0
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +41 -0
- package/templates/init/apptesting/smoke_test.yaml +1 -1
- package/templates/init/dataconnect/secondary_schema.gql +8 -0
- package/templates/init/functions/javascript/index-ongraphrequest.js +36 -0
- package/templates/init/functions/javascript/package-ongraphrequest.lint.json +28 -0
- package/templates/init/functions/javascript/package-ongraphrequest.nolint.json +25 -0
- package/templates/init/functions/python/requirements.txt +1 -1
- package/templates/init/functions/typescript/index-ongraphrequest.ts +44 -0
- package/templates/init/functions/typescript/package-ongraphrequest.lint.json +33 -0
- package/templates/init/functions/typescript/package-ongraphrequest.nolint.json +27 -0
- package/lib/mcp/util.test.js +0 -468
|
@@ -82,6 +82,9 @@ function assertBuildEndpoint(ep, id) {
|
|
|
82
82
|
taskQueueTrigger: "object",
|
|
83
83
|
blockingTrigger: "object",
|
|
84
84
|
cpu: (cpu) => cpu === null || isCEL(cpu) || cpu === "gcf_gen1" || typeof cpu === "number",
|
|
85
|
+
baseImageUri: "string?",
|
|
86
|
+
command: "array?",
|
|
87
|
+
args: "array?",
|
|
85
88
|
});
|
|
86
89
|
if (ep.vpc) {
|
|
87
90
|
(0, parsing_1.assertKeyTypes)(prefix + ".vpc", ep.vpc, {
|
|
@@ -294,7 +297,7 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
|
|
|
294
297
|
if ("serviceAccountEmail" in ep) {
|
|
295
298
|
parsed.serviceAccount = ep.serviceAccountEmail;
|
|
296
299
|
}
|
|
297
|
-
(0, proto_1.copyIfPresent)(parsed, ep, "omit", "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount");
|
|
300
|
+
(0, proto_1.copyIfPresent)(parsed, ep, "omit", "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount", "baseImageUri", "command", "args");
|
|
298
301
|
(0, proto_1.convertIfPresent)(parsed, ep, "secretEnvironmentVariables", (senvs) => {
|
|
299
302
|
if (!senvs) {
|
|
300
303
|
return null;
|
|
@@ -6,7 +6,15 @@ const python = require("./python");
|
|
|
6
6
|
const validate = require("../validate");
|
|
7
7
|
const error_1 = require("../../../error");
|
|
8
8
|
const supported = require("./supported");
|
|
9
|
-
const
|
|
9
|
+
const dart = require("./dart");
|
|
10
|
+
const experiments = require("../../../experiments");
|
|
11
|
+
const factories = [
|
|
12
|
+
node.tryCreateDelegate,
|
|
13
|
+
python.tryCreateDelegate,
|
|
14
|
+
(ctx) => experiments.isEnabled("functionsrunapionly")
|
|
15
|
+
? dart.tryCreateDelegate(ctx)
|
|
16
|
+
: Promise.resolve(undefined),
|
|
17
|
+
];
|
|
10
18
|
async function getRuntimeDelegate(context) {
|
|
11
19
|
const { projectDir, sourceDir, runtime } = context;
|
|
12
20
|
if (runtime && !supported.isRuntime(runtime)) {
|
package/lib/deploy/index.js
CHANGED
|
@@ -21,6 +21,7 @@ const RemoteConfigTarget = require("./remoteconfig");
|
|
|
21
21
|
const ExtensionsTarget = require("./extensions");
|
|
22
22
|
const DataConnectTarget = require("./dataconnect");
|
|
23
23
|
const AppHostingTarget = require("./apphosting");
|
|
24
|
+
const AuthTarget = require("./auth");
|
|
24
25
|
const frameworks_1 = require("../frameworks");
|
|
25
26
|
const prepare_1 = require("./hosting/prepare");
|
|
26
27
|
const utils_2 = require("../utils");
|
|
@@ -37,6 +38,7 @@ const TARGETS = {
|
|
|
37
38
|
extensions: ExtensionsTarget,
|
|
38
39
|
dataconnect: DataConnectTarget,
|
|
39
40
|
apphosting: AppHostingTarget,
|
|
41
|
+
auth: AuthTarget,
|
|
40
42
|
};
|
|
41
43
|
const chain = async function (fns, context, options, payload) {
|
|
42
44
|
for (const latest of fns) {
|
|
@@ -69,23 +69,29 @@ class AuthEmulator {
|
|
|
69
69
|
else {
|
|
70
70
|
logger.logLabeled("WARN", "auth", `Skipped importing config because ${configPath} does not exist.`);
|
|
71
71
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
const accountFiles = fs
|
|
73
|
+
.readdirSync(authExportDir)
|
|
74
|
+
.filter((fileName) => fileName.includes("accounts"));
|
|
75
|
+
for (const accountFile of accountFiles) {
|
|
76
|
+
const accountsPath = path.join(authExportDir, accountFile);
|
|
77
|
+
const accountsStat = await stat(accountsPath);
|
|
78
|
+
const tenantId = accountFile.replace(/accounts(-|)|.json/gm, "");
|
|
79
|
+
if (accountsStat?.isFile()) {
|
|
80
|
+
logger.logLabeled("BULLET", "auth", `Importing accounts from ${accountsPath}`);
|
|
81
|
+
await importFromFile({
|
|
82
|
+
method: "POST",
|
|
83
|
+
host: utils.connectableHostname(host),
|
|
84
|
+
port,
|
|
85
|
+
path: `/identitytoolkit.googleapis.com/v1/projects/${projectId}/tenants/${tenantId}/accounts:batchCreate`,
|
|
86
|
+
headers: {
|
|
87
|
+
Authorization: "Bearer owner",
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
},
|
|
90
|
+
}, accountsPath, { ignoreErrors: ["MISSING_USER_ACCOUNT"] });
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
logger.logLabeled("WARN", "auth", `Skipped importing accounts because ${accountsPath} does not exist.`);
|
|
94
|
+
}
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
}
|
|
@@ -426,7 +426,15 @@ function batchDelete(state, reqBody) {
|
|
|
426
426
|
function batchGet(state, reqBody, ctx) {
|
|
427
427
|
(0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
|
|
428
428
|
const maxResults = Math.min(Math.floor(ctx.params.query.maxResults) || 20, 1000);
|
|
429
|
-
const
|
|
429
|
+
const queryTenantId = ctx.params.query.tenantId;
|
|
430
|
+
let users;
|
|
431
|
+
if (queryTenantId && state instanceof state_1.AgentProjectState) {
|
|
432
|
+
const tenant = state.getTenantProject(queryTenantId);
|
|
433
|
+
users = tenant.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
|
|
437
|
+
}
|
|
430
438
|
let newPageToken = undefined;
|
|
431
439
|
if (maxResults >= 0 && users.length >= maxResults) {
|
|
432
440
|
users.length = maxResults;
|
|
@@ -44,46 +44,46 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"pubsub": {
|
|
47
|
-
"version": "0.8.
|
|
48
|
-
"expectedSize":
|
|
49
|
-
"expectedChecksum": "
|
|
50
|
-
"expectedChecksumSHA256": "
|
|
51
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.
|
|
52
|
-
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
53
|
-
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
47
|
+
"version": "0.8.27",
|
|
48
|
+
"expectedSize": 52924291,
|
|
49
|
+
"expectedChecksum": "cb0d35db6aa1bb5e3f7e2a5a690c631d",
|
|
50
|
+
"expectedChecksumSHA256": "0b793b420b608b68c200a0d15123c63967ac2863bbd9545ecb087d5b28871339",
|
|
51
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.27.zip",
|
|
52
|
+
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.27.zip",
|
|
53
|
+
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.27/pubsub-emulator/bin/cloud-pubsub-emulator"
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.1.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.1.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.
|
|
57
|
+
"version": "3.1.4",
|
|
58
|
+
"expectedSize": 30761824,
|
|
59
|
+
"expectedChecksum": "199b9bd400ab42c549f19254c668e148",
|
|
60
|
+
"expectedChecksumSHA256": "6f972b594708978b7d42348007de1d1614e6f43b583167e05bf8f4d4eff99173",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.1.4",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.4"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.1.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.1.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.
|
|
65
|
+
"version": "3.1.4",
|
|
66
|
+
"expectedSize": 30236450,
|
|
67
|
+
"expectedChecksum": "0f59789d0c62ec4a0135d356187fe542",
|
|
68
|
+
"expectedChecksumSHA256": "59d590c7b60598be4d86155451abc55fbf0f65801ba11c06900accf491977e69",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.1.4",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.4"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.1.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.1.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.
|
|
73
|
+
"version": "3.1.4",
|
|
74
|
+
"expectedSize": 31271424,
|
|
75
|
+
"expectedChecksum": "bd394b8080c27f02fb7eaf898b0f34a2",
|
|
76
|
+
"expectedChecksumSHA256": "7a102aa870512782c453c0aac9811930170cd69659b352ec7d873f0db674849a",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.1.4",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.4.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.1.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.1.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.
|
|
81
|
+
"version": "3.1.4",
|
|
82
|
+
"expectedSize": 30691512,
|
|
83
|
+
"expectedChecksum": "94ebf4c312dcf83f6cefc9d61055579f",
|
|
84
|
+
"expectedChecksumSHA256": "7dca7fdc2d702c44a2eaa017728ded8358abb90e4bfaeb5bad1aae51181abd58",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.1.4",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.4"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -166,8 +166,22 @@ class HubExport {
|
|
|
166
166
|
if (!fs.existsSync(authExportPath)) {
|
|
167
167
|
fs.mkdirSync(authExportPath);
|
|
168
168
|
}
|
|
169
|
+
const tenantsRes = await registry_1.EmulatorRegistry.client(types_1.Emulators.AUTH).get(`/identitytoolkit.googleapis.com/v2/projects/${this.projectId}/tenants`, {
|
|
170
|
+
headers: { Authorization: "Bearer owner" },
|
|
171
|
+
});
|
|
172
|
+
const tenants = tenantsRes.body.tenants.map((instance) => instance.tenantId);
|
|
173
|
+
for (const tenantId of tenants) {
|
|
174
|
+
const accountsFile = path.join(authExportPath, `accounts-${tenantId}.json`);
|
|
175
|
+
logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} ${tenantId} tenant to ${accountsFile}`);
|
|
176
|
+
await fetchToFile({
|
|
177
|
+
host,
|
|
178
|
+
port,
|
|
179
|
+
path: `/identitytoolkit.googleapis.com/v1/projects/${this.projectId}/accounts:batchGet?maxResults=-1&tenantId=${tenantId}`,
|
|
180
|
+
headers: { Authorization: "Bearer owner" },
|
|
181
|
+
}, accountsFile);
|
|
182
|
+
}
|
|
169
183
|
const accountsFile = path.join(authExportPath, "accounts.json");
|
|
170
|
-
logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} to ${accountsFile}`);
|
|
184
|
+
logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} default tenant to ${accountsFile}`);
|
|
171
185
|
await fetchToFile({
|
|
172
186
|
host,
|
|
173
187
|
port,
|
package/lib/gcp/cloudbilling.js
CHANGED
|
@@ -20,16 +20,19 @@ async function isBillingEnabled(setup) {
|
|
|
20
20
|
return setup.isBillingEnabled;
|
|
21
21
|
}
|
|
22
22
|
async function checkBillingEnabled(projectId) {
|
|
23
|
-
const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), {
|
|
23
|
+
const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), {
|
|
24
|
+
retries: 3,
|
|
25
|
+
retryCodes: [429, 500, 503],
|
|
26
|
+
});
|
|
24
27
|
return res.body.billingEnabled;
|
|
25
28
|
}
|
|
26
29
|
async function setBillingAccount(projectId, billingAccountName) {
|
|
27
30
|
const res = await client.put(utils.endpoint(["projects", projectId, "billingInfo"]), {
|
|
28
31
|
billingAccountName: billingAccountName,
|
|
29
|
-
}, { retryCodes: [500, 503] });
|
|
32
|
+
}, { retryCodes: [429, 500, 503] });
|
|
30
33
|
return res.body.billingEnabled;
|
|
31
34
|
}
|
|
32
35
|
async function listBillingAccounts() {
|
|
33
|
-
const res = await client.get(utils.endpoint(["billingAccounts"]), { retryCodes: [500, 503] });
|
|
36
|
+
const res = await client.get(utils.endpoint(["billingAccounts"]), { retryCodes: [429, 500, 503] });
|
|
34
37
|
return res.body.billingAccounts || [];
|
|
35
38
|
}
|
package/lib/gcp/runv2.js
CHANGED
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FIREBASE_FUNCTION_METADTA_ANNOTATION = exports.FUNCTION_SIGNATURE_TYPE_ENV = exports.FUNCTION_TARGET_ENV = exports.FUNCTION_ID_ANNOTATION = exports.FUNCTION_TARGET_ANNOTATION = exports.TRIGGER_TYPE_ANNOTATION = exports.CLIENT_NAME_LABEL = exports.RUNTIME_LABEL = exports.API_VERSION = void 0;
|
|
4
4
|
exports.submitBuild = submitBuild;
|
|
5
5
|
exports.updateService = updateService;
|
|
6
|
+
exports.createService = createService;
|
|
7
|
+
exports.deleteService = deleteService;
|
|
8
|
+
exports.getService = getService;
|
|
6
9
|
exports.listServices = listServices;
|
|
7
10
|
exports.endpointFromService = endpointFromService;
|
|
8
11
|
exports.serviceFromEndpoint = serviceFromEndpoint;
|
|
@@ -51,6 +54,34 @@ async function updateService(service) {
|
|
|
51
54
|
});
|
|
52
55
|
return svc;
|
|
53
56
|
}
|
|
57
|
+
async function createService(projectId, location, serviceId, service) {
|
|
58
|
+
const { name, ...serviceBody } = service;
|
|
59
|
+
const res = await client.post(`/projects/${projectId}/locations/${location}/services`, serviceBody, {
|
|
60
|
+
queryParams: {
|
|
61
|
+
serviceId,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const svc = await (0, operation_poller_1.pollOperation)({
|
|
65
|
+
apiOrigin: (0, api_1.runOrigin)(),
|
|
66
|
+
apiVersion: exports.API_VERSION,
|
|
67
|
+
operationResourceName: res.body.name,
|
|
68
|
+
});
|
|
69
|
+
return svc;
|
|
70
|
+
}
|
|
71
|
+
async function deleteService(projectId, location, serviceId) {
|
|
72
|
+
const name = `projects/${projectId}/locations/${location}/services/${serviceId}`;
|
|
73
|
+
const res = await client.delete(name);
|
|
74
|
+
await (0, operation_poller_1.pollOperation)({
|
|
75
|
+
apiOrigin: (0, api_1.runOrigin)(),
|
|
76
|
+
apiVersion: exports.API_VERSION,
|
|
77
|
+
operationResourceName: res.body.name,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function getService(projectId, location, serviceId) {
|
|
81
|
+
const name = `projects/${projectId}/locations/${location}/services/${serviceId}`;
|
|
82
|
+
const res = await client.get(name);
|
|
83
|
+
return res.body;
|
|
84
|
+
}
|
|
54
85
|
async function listServices(projectId) {
|
|
55
86
|
const allServices = [];
|
|
56
87
|
let pageToken = undefined;
|
|
@@ -115,6 +146,15 @@ function endpointFromService(service) {
|
|
|
115
146
|
service.annotations?.[exports.FUNCTION_TARGET_ANNOTATION] ||
|
|
116
147
|
service.annotations?.[exports.FUNCTION_ID_ANNOTATION] ||
|
|
117
148
|
id,
|
|
149
|
+
timeoutSeconds: service.template.timeout
|
|
150
|
+
? proto.secondsFromDuration(service.template.timeout)
|
|
151
|
+
: 60,
|
|
152
|
+
serviceAccount: service.template.serviceAccount || null,
|
|
153
|
+
ingressSettings: (service.annotations?.["run.googleapis.com/ingress"] === "internal"
|
|
154
|
+
? "ALLOW_INTERNAL_ONLY"
|
|
155
|
+
: service.annotations?.["run.googleapis.com/ingress"] === "internal-and-cloud-load-balancing"
|
|
156
|
+
? "ALLOW_INTERNAL_AND_GCLB"
|
|
157
|
+
: "ALLOW_ALL"),
|
|
118
158
|
...(service.annotations?.[exports.TRIGGER_TYPE_ANNOTATION] === "HTTP_TRIGGER"
|
|
119
159
|
? { httpsTrigger: {} }
|
|
120
160
|
: {
|
|
@@ -124,7 +164,7 @@ function endpointFromService(service) {
|
|
|
124
164
|
},
|
|
125
165
|
}),
|
|
126
166
|
};
|
|
127
|
-
proto.renameIfPresent(endpoint, service.template, "concurrency", "
|
|
167
|
+
proto.renameIfPresent(endpoint, service.template, "concurrency", "maxInstanceRequestConcurrency");
|
|
128
168
|
proto.renameIfPresent(endpoint, service.labels || {}, "codebase", constants_1.CODEBASE_LABEL);
|
|
129
169
|
proto.renameIfPresent(endpoint, service.scaling || {}, "minInstances", "minInstanceCount");
|
|
130
170
|
proto.renameIfPresent(endpoint, service.scaling || {}, "maxInstances", "maxInstanceCount");
|
|
@@ -141,6 +181,12 @@ function endpointFromService(service) {
|
|
|
141
181
|
version: e.valueSource.secretKeyRef.version || "latest",
|
|
142
182
|
};
|
|
143
183
|
});
|
|
184
|
+
if (service.template.vpcAccess) {
|
|
185
|
+
endpoint.vpc = {
|
|
186
|
+
connector: service.template.vpcAccess.connector || "",
|
|
187
|
+
egressSettings: service.template.vpcAccess.egress || null,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
144
190
|
return endpoint;
|
|
145
191
|
}
|
|
146
192
|
function serviceFromEndpoint(endpoint, image) {
|
|
@@ -194,11 +240,14 @@ function serviceFromEndpoint(endpoint, image) {
|
|
|
194
240
|
cpuIdle: true,
|
|
195
241
|
startupCpuBoost: true,
|
|
196
242
|
},
|
|
243
|
+
...(endpoint.baseImageUri ? { baseImageUri: endpoint.baseImageUri } : {}),
|
|
244
|
+
...(endpoint.command ? { command: endpoint.command } : {}),
|
|
245
|
+
...(endpoint.args ? { args: endpoint.args } : {}),
|
|
197
246
|
},
|
|
198
247
|
],
|
|
199
|
-
|
|
248
|
+
maxInstanceRequestConcurrency: endpoint.concurrency || backend.DEFAULT_CONCURRENCY,
|
|
200
249
|
};
|
|
201
|
-
proto.renameIfPresent(template, endpoint, "
|
|
250
|
+
proto.renameIfPresent(template, endpoint, "maxInstanceRequestConcurrency", "concurrency");
|
|
202
251
|
const service = {
|
|
203
252
|
name: `projects/${endpoint.project}/locations/${endpoint.region}/services/${functionNameToServiceName(endpoint.id)}`,
|
|
204
253
|
labels,
|
|
@@ -211,5 +260,28 @@ function serviceFromEndpoint(endpoint, image) {
|
|
|
211
260
|
proto.renameIfPresent(service.scaling, endpoint, "minInstanceCount", "minInstances");
|
|
212
261
|
proto.renameIfPresent(service.scaling, endpoint, "maxInstanceCount", "maxInstances");
|
|
213
262
|
}
|
|
263
|
+
if (endpoint.serviceAccount) {
|
|
264
|
+
template.serviceAccount = endpoint.serviceAccount;
|
|
265
|
+
}
|
|
266
|
+
if (endpoint.timeoutSeconds) {
|
|
267
|
+
template.timeout = proto.durationFromSeconds(endpoint.timeoutSeconds);
|
|
268
|
+
}
|
|
269
|
+
if (endpoint.vpc) {
|
|
270
|
+
template.vpcAccess = {
|
|
271
|
+
connector: endpoint.vpc.connector,
|
|
272
|
+
egress: endpoint.vpc.egressSettings || undefined,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (endpoint.ingressSettings) {
|
|
276
|
+
const ingressAnnotation = endpoint.ingressSettings === "ALLOW_INTERNAL_ONLY"
|
|
277
|
+
? "internal"
|
|
278
|
+
: endpoint.ingressSettings === "ALLOW_INTERNAL_AND_GCLB"
|
|
279
|
+
? "internal-and-cloud-load-balancing"
|
|
280
|
+
: "all";
|
|
281
|
+
service.annotations = {
|
|
282
|
+
...service.annotations,
|
|
283
|
+
"run.googleapis.com/ingress": ingressAnnotation,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
214
286
|
return service;
|
|
215
287
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PROMPT_GENERATE_SEED_DATA = exports.PROMPT_GENERATE_CONNECTOR = void 0;
|
|
4
4
|
exports.generateSchema = generateSchema;
|
|
5
|
-
exports.chatWithFirebase = chatWithFirebase;
|
|
6
5
|
exports.generateOperation = generateOperation;
|
|
7
6
|
exports.extractCodeBlock = extractCodeBlock;
|
|
8
7
|
const apiv2_1 = require("../apiv2");
|
|
@@ -10,7 +9,6 @@ const api_1 = require("../api");
|
|
|
10
9
|
const error_1 = require("../error");
|
|
11
10
|
const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudAiCompanionOrigin)(), auth: true });
|
|
12
11
|
const SCHEMA_GENERATOR_EXPERIENCE = "/appeco/firebase/fdc-schema-generator";
|
|
13
|
-
const GEMINI_IN_FIREBASE_EXPERIENCE = "/appeco/firebase/firebase-chat/free";
|
|
14
12
|
const OPERATION_GENERATION_EXPERIENCE = "/appeco/firebase/fdc-query-generator";
|
|
15
13
|
const FIREBASE_CHAT_REQUEST_CONTEXT_TYPE_NAME = "type.googleapis.com/google.cloud.cloudaicompanion.v1main.FirebaseChatRequestContext";
|
|
16
14
|
exports.PROMPT_GENERATE_CONNECTOR = "Create 4 operations for an app using the instance schema with proper authentication.";
|
|
@@ -24,15 +22,6 @@ async function generateSchema(prompt, project, chatHistory = []) {
|
|
|
24
22
|
});
|
|
25
23
|
return extractCodeBlock(res.body.output.messages[0].content);
|
|
26
24
|
}
|
|
27
|
-
async function chatWithFirebase(prompt, project, chatHistory = []) {
|
|
28
|
-
const res = await apiClient.post(`/v1beta/projects/${project}/locations/global/instances/default:completeTask`, {
|
|
29
|
-
input: { messages: [...chatHistory, { content: prompt, author: "USER" }] },
|
|
30
|
-
experienceContext: {
|
|
31
|
-
experience: GEMINI_IN_FIREBASE_EXPERIENCE,
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
return res.body;
|
|
35
|
-
}
|
|
36
25
|
async function generateOperation(prompt, service, project, chatHistory = []) {
|
|
37
26
|
const res = await apiClient.post(`/v1beta/projects/${project}/locations/global/instances/default:completeTask`, {
|
|
38
27
|
input: { messages: [...chatHistory, { content: prompt, author: "USER" }] },
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.askQuestions = askQuestions;
|
|
4
|
+
exports.actuate = actuate;
|
|
5
|
+
const clc = require("colorette");
|
|
6
|
+
const prompt_1 = require("../../prompt");
|
|
7
|
+
const logger_1 = require("../../logger");
|
|
8
|
+
async function askQuestions(setup) {
|
|
9
|
+
const authConfig = setup.config.auth;
|
|
10
|
+
const choices = [
|
|
11
|
+
{
|
|
12
|
+
name: "Google Sign-In",
|
|
13
|
+
value: "google",
|
|
14
|
+
checked: !!authConfig?.providers?.googleSignIn,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "Email/Password",
|
|
18
|
+
value: "email",
|
|
19
|
+
checked: !!authConfig?.providers?.emailPassword,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "Anonymous",
|
|
23
|
+
value: "anonymous",
|
|
24
|
+
checked: !!authConfig?.providers?.anonymous,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
const providers = await (0, prompt_1.checkbox)({
|
|
28
|
+
message: "Which providers would you like to enable? If you don't see a provider here, go to the Firebase Console to set it up.",
|
|
29
|
+
choices: choices,
|
|
30
|
+
});
|
|
31
|
+
const providersConfig = {};
|
|
32
|
+
if (providers.includes("anonymous")) {
|
|
33
|
+
providersConfig.anonymous = true;
|
|
34
|
+
}
|
|
35
|
+
if (providers.includes("email")) {
|
|
36
|
+
providersConfig.emailPassword = true;
|
|
37
|
+
}
|
|
38
|
+
if (providers.includes("google")) {
|
|
39
|
+
logger_1.logger.info("");
|
|
40
|
+
logger_1.logger.info("Configuring Google Sign-In...");
|
|
41
|
+
const oAuthBrandDisplayName = await (0, prompt_1.input)({
|
|
42
|
+
message: "What display name would you like to use for your OAuth brand?",
|
|
43
|
+
default: authConfig?.providers?.googleSignIn?.oAuthBrandDisplayName ||
|
|
44
|
+
setup.project?.projectId ||
|
|
45
|
+
"My App",
|
|
46
|
+
});
|
|
47
|
+
const supportEmail = await (0, prompt_1.input)({
|
|
48
|
+
message: "What support email would you like to register for your OAuth brand?",
|
|
49
|
+
default: authConfig?.providers?.googleSignIn?.supportEmail ||
|
|
50
|
+
(setup.project ? `support@${setup.project.projectId}.firebaseapp.com` : undefined),
|
|
51
|
+
});
|
|
52
|
+
providersConfig.googleSignIn = {
|
|
53
|
+
oAuthBrandDisplayName,
|
|
54
|
+
supportEmail,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!setup.featureInfo) {
|
|
58
|
+
setup.featureInfo = {};
|
|
59
|
+
}
|
|
60
|
+
setup.featureInfo.auth = { providers: providersConfig };
|
|
61
|
+
}
|
|
62
|
+
async function actuate(setup, config) {
|
|
63
|
+
const authConfig = setup.featureInfo?.auth;
|
|
64
|
+
if (!authConfig) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
config.set("auth", authConfig);
|
|
68
|
+
config.writeProjectFile("firebase.json", config.src);
|
|
69
|
+
logger_1.logger.info("");
|
|
70
|
+
logger_1.logger.info("Generated firebase.json with auth configuration.");
|
|
71
|
+
logger_1.logger.info("Run " + clc.bold("firebase deploy") + " to enable these providers.");
|
|
72
|
+
}
|
|
@@ -4,7 +4,6 @@ exports.askQuestions = askQuestions;
|
|
|
4
4
|
exports.actuate = actuate;
|
|
5
5
|
exports.addSchemaToDataConnectYaml = addSchemaToDataConnectYaml;
|
|
6
6
|
const clc = require("colorette");
|
|
7
|
-
const fs = require("fs-extra");
|
|
8
7
|
const path_1 = require("path");
|
|
9
8
|
const yaml = require("yaml");
|
|
10
9
|
const prompt_1 = require("../../../prompt");
|
|
@@ -14,11 +13,15 @@ const names_1 = require("../../../dataconnect/names");
|
|
|
14
13
|
const experiments = require("../../../experiments");
|
|
15
14
|
const cloudbilling_1 = require("../../../gcp/cloudbilling");
|
|
16
15
|
const track_1 = require("../../../track");
|
|
17
|
-
|
|
16
|
+
const functions = require("../functions");
|
|
17
|
+
const templates_1 = require("../../../templates");
|
|
18
|
+
const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/secondary_schema.gql");
|
|
19
|
+
async function askQuestions(setup, config, options) {
|
|
18
20
|
const resolverInfo = {
|
|
19
21
|
id: "",
|
|
20
22
|
uri: "",
|
|
21
23
|
serviceInfo: {},
|
|
24
|
+
shouldInitFunctions: false,
|
|
22
25
|
};
|
|
23
26
|
const serviceInfos = await (0, load_1.loadAll)(setup.projectId || "", config);
|
|
24
27
|
if (!serviceInfos.length) {
|
|
@@ -50,8 +53,15 @@ async function askQuestions(setup, config) {
|
|
|
50
53
|
" as the custom resolver URL. To change this, update your " +
|
|
51
54
|
clc.bold(`dataconnect.yaml`) +
|
|
52
55
|
" later.");
|
|
56
|
+
resolverInfo.shouldInitFunctions = await (0, prompt_1.confirm)({
|
|
57
|
+
message: "Would you like to proceed with initializing a functions codebase with sample custom resolvers code?",
|
|
58
|
+
default: true,
|
|
59
|
+
});
|
|
53
60
|
setup.featureInfo = setup.featureInfo || {};
|
|
54
61
|
setup.featureInfo.dataconnectResolver = resolverInfo;
|
|
62
|
+
if (resolverInfo.shouldInitFunctions) {
|
|
63
|
+
await functions.askQuestions(setup, config, options);
|
|
64
|
+
}
|
|
55
65
|
}
|
|
56
66
|
async function actuate(setup, config) {
|
|
57
67
|
if (!experiments.isEnabled("fdcwebhooks")) {
|
|
@@ -76,6 +86,9 @@ async function actuate(setup, config) {
|
|
|
76
86
|
: "missing",
|
|
77
87
|
}, Date.now() - startTime);
|
|
78
88
|
}
|
|
89
|
+
if (resolverInfo.shouldInitFunctions) {
|
|
90
|
+
await functions.actuate(setup, config);
|
|
91
|
+
}
|
|
79
92
|
}
|
|
80
93
|
function actuateWithInfo(config, info) {
|
|
81
94
|
const dataConnectYaml = JSON.parse(JSON.stringify(info.serviceInfo?.dataConnectYaml));
|
|
@@ -83,8 +96,9 @@ function actuateWithInfo(config, info) {
|
|
|
83
96
|
info.serviceInfo.dataConnectYaml = dataConnectYaml;
|
|
84
97
|
const dataConnectYamlContents = yaml.stringify(dataConnectYaml);
|
|
85
98
|
const dataConnectYamlPath = (0, path_1.join)(info.serviceInfo.sourceDirectory, "dataconnect.yaml");
|
|
99
|
+
const dataConnectSchemaPath = (0, path_1.join)(info.serviceInfo.sourceDirectory, `schema_${info.id}`, "schema.gql");
|
|
86
100
|
config.writeProjectFile((0, path_1.relative)(config.projectDir, dataConnectYamlPath), dataConnectYamlContents);
|
|
87
|
-
|
|
101
|
+
config.writeProjectFile((0, path_1.relative)(config.projectDir, dataConnectSchemaPath), SCHEMA_TEMPLATE);
|
|
88
102
|
}
|
|
89
103
|
function addSchemaToDataConnectYaml(dataConnectYaml, info) {
|
|
90
104
|
const secondarySchema = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.askQuestions = askQuestions;
|
|
4
|
+
exports.actuate = actuate;
|
|
4
5
|
const clc = require("colorette");
|
|
5
6
|
const logger_1 = require("../../../logger");
|
|
6
7
|
const prompt_1 = require("../../../prompt");
|
|
@@ -11,7 +12,7 @@ const error_1 = require("../../../error");
|
|
|
11
12
|
const api_1 = require("../../../api");
|
|
12
13
|
const supported = require("../../../deploy/functions/runtimes/supported");
|
|
13
14
|
const MAX_ATTEMPTS = 5;
|
|
14
|
-
async function
|
|
15
|
+
async function askQuestions(setup, config, options) {
|
|
15
16
|
const projectId = setup?.rcfile?.projects?.default;
|
|
16
17
|
if (projectId) {
|
|
17
18
|
await (0, requirePermissions_1.requirePermissions)({ ...options, project: projectId });
|
|
@@ -23,7 +24,7 @@ async function doSetup(setup, config, options) {
|
|
|
23
24
|
setup.functions = {};
|
|
24
25
|
if (!config.src.functions) {
|
|
25
26
|
setup.config.functions = [];
|
|
26
|
-
return initNewCodebase(setup
|
|
27
|
+
return initNewCodebase(setup);
|
|
27
28
|
}
|
|
28
29
|
setup.config.functions = (0, projectConfig_1.normalizeAndValidate)(setup.config.functions);
|
|
29
30
|
const codebases = setup.config.functions.map((cfg) => clc.bold(cfg.codebase));
|
|
@@ -43,9 +44,9 @@ async function doSetup(setup, config, options) {
|
|
|
43
44
|
default: "new",
|
|
44
45
|
choices,
|
|
45
46
|
});
|
|
46
|
-
return initOpt === "new" ? initNewCodebase(setup
|
|
47
|
+
return initOpt === "new" ? initNewCodebase(setup) : overwriteCodebase(setup);
|
|
47
48
|
}
|
|
48
|
-
async function initNewCodebase(setup
|
|
49
|
+
async function initNewCodebase(setup) {
|
|
49
50
|
logger_1.logger.info("Let's create a new codebase for your functions.");
|
|
50
51
|
logger_1.logger.info("A directory corresponding to the codebase will be created in your project");
|
|
51
52
|
logger_1.logger.info("with sample code pre-configured.\n");
|
|
@@ -100,9 +101,9 @@ async function initNewCodebase(setup, config) {
|
|
|
100
101
|
});
|
|
101
102
|
setup.functions.source = source;
|
|
102
103
|
setup.functions.codebase = codebase;
|
|
103
|
-
return languageSetup(setup
|
|
104
|
+
return languageSetup(setup);
|
|
104
105
|
}
|
|
105
|
-
async function overwriteCodebase(setup
|
|
106
|
+
async function overwriteCodebase(setup) {
|
|
106
107
|
let codebase;
|
|
107
108
|
if (setup.config.functions.length > 1) {
|
|
108
109
|
const choices = setup.config.functions.map((cfg) => ({
|
|
@@ -121,11 +122,11 @@ async function overwriteCodebase(setup, config) {
|
|
|
121
122
|
setup.functions.source = cbconfig.source;
|
|
122
123
|
setup.functions.codebase = cbconfig.codebase;
|
|
123
124
|
logger_1.logger.info(`\nOverwriting ${clc.bold(`codebase ${codebase}...\n`)}`);
|
|
124
|
-
return languageSetup(setup
|
|
125
|
+
return languageSetup(setup);
|
|
125
126
|
}
|
|
126
|
-
async function languageSetup(setup
|
|
127
|
+
async function languageSetup(setup) {
|
|
127
128
|
if (setup.languageOverride) {
|
|
128
|
-
return
|
|
129
|
+
return;
|
|
129
130
|
}
|
|
130
131
|
const choices = [
|
|
131
132
|
{
|
|
@@ -136,11 +137,13 @@ async function languageSetup(setup, config) {
|
|
|
136
137
|
name: "TypeScript",
|
|
137
138
|
value: "typescript",
|
|
138
139
|
},
|
|
139
|
-
|
|
140
|
+
];
|
|
141
|
+
if (!setup.featureInfo?.dataconnectResolver) {
|
|
142
|
+
choices.push({
|
|
140
143
|
name: "Python",
|
|
141
144
|
value: "python",
|
|
142
|
-
}
|
|
143
|
-
|
|
145
|
+
});
|
|
146
|
+
}
|
|
144
147
|
const language = await (0, prompt_1.select)({
|
|
145
148
|
message: "What language would you like to use to write Cloud Functions?",
|
|
146
149
|
default: "javascript",
|
|
@@ -172,5 +175,10 @@ async function languageSetup(setup, config) {
|
|
|
172
175
|
break;
|
|
173
176
|
}
|
|
174
177
|
setup.functions.languageChoice = language;
|
|
175
|
-
|
|
178
|
+
}
|
|
179
|
+
async function actuate(setup, config) {
|
|
180
|
+
if (setup.languageOverride) {
|
|
181
|
+
return require("./" + setup.languageOverride).setup(setup, config);
|
|
182
|
+
}
|
|
183
|
+
return require("./" + setup.functions.languageChoice).setup(setup, config);
|
|
176
184
|
}
|