firebase-tools 15.5.0 → 15.6.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/apiv2.js +11 -5
- package/lib/apphosting/backend.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/ensureApiEnabled.js +2 -2
- package/lib/gcp/cloudbilling.js +6 -3
- package/lib/gcp/iam.js +1 -1
- package/lib/gcp/runv2.js +75 -3
- package/lib/gcp/serviceusage.js +2 -2
- 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/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/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/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
|
@@ -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.3",
|
|
58
|
+
"expectedSize": 30753632,
|
|
59
|
+
"expectedChecksum": "db15fbb3d596b056390c08d065500c63",
|
|
60
|
+
"expectedChecksumSHA256": "f1b98402e9d8497e88a96de6ac80425aee3a61513eccc1fb76b4e387862625ee",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.1.3",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
|
|
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.3",
|
|
66
|
+
"expectedSize": 30203330,
|
|
67
|
+
"expectedChecksum": "8ac08faf1b797615d91c58f7ea781af0",
|
|
68
|
+
"expectedChecksumSHA256": "eb00f5bd3c97c17b617a9ce5f5153d6d9f20bc482e4ac091a6361f9ff3bd8841",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.1.3",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
|
|
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.3",
|
|
74
|
+
"expectedSize": 31262208,
|
|
75
|
+
"expectedChecksum": "b6723bed0937b1a3fed6fdb64df39f4d",
|
|
76
|
+
"expectedChecksumSHA256": "3d213e26ab7ea4cd44083a32d882128e0ff9313a69795d88cb7a9f31042675ff",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.1.3",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3.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.3",
|
|
82
|
+
"expectedSize": 30679224,
|
|
83
|
+
"expectedChecksum": "f1d0f701d8e56669a93f00f1c10a7437",
|
|
84
|
+
"expectedChecksumSHA256": "826aa0fcbb28bad7ced433e6386ea1cce2407357a145da7111e800489c6cf3c9",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.1.3",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
|
|
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/ensureApiEnabled.js
CHANGED
|
@@ -27,7 +27,7 @@ async function check(projectId, apiUri, prefix, silent = false) {
|
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
29
|
const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
|
|
30
|
-
headers: { "x-goog-user-project":
|
|
30
|
+
headers: { "x-goog-user-project": `${projectId}` },
|
|
31
31
|
skipLog: { resBody: true },
|
|
32
32
|
});
|
|
33
33
|
const isEnabled = res.body.state === "ENABLED";
|
|
@@ -45,7 +45,7 @@ function isPermissionError(e) {
|
|
|
45
45
|
async function enable(projectId, apiName) {
|
|
46
46
|
try {
|
|
47
47
|
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
|
|
48
|
-
headers: { "x-goog-user-project":
|
|
48
|
+
headers: { "x-goog-user-project": `${projectId}` },
|
|
49
49
|
skipLog: { resBody: true },
|
|
50
50
|
});
|
|
51
51
|
cacheEnabledAPI(projectId, apiName);
|
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/iam.js
CHANGED
|
@@ -78,7 +78,7 @@ async function testResourceIamPermissions(origin, apiVersion, resourceName, perm
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
async function testIamPermissions(projectId, permissions) {
|
|
81
|
-
return testResourceIamPermissions((0, api_1.resourceManagerOrigin)(), "v1", `projects/${projectId}`, permissions,
|
|
81
|
+
return testResourceIamPermissions((0, api_1.resourceManagerOrigin)(), "v1", `projects/${projectId}`, permissions, `${projectId}`);
|
|
82
82
|
}
|
|
83
83
|
function mergeBindings(policy, requiredBindings) {
|
|
84
84
|
let updated = false;
|
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
|
}
|
package/lib/gcp/serviceusage.js
CHANGED
|
@@ -22,7 +22,7 @@ const serviceUsagePollerOptions = {
|
|
|
22
22
|
async function generateServiceIdentity(projectNumber, service, prefix) {
|
|
23
23
|
utils.logLabeledBullet(prefix, `generating the service identity for ${(0, colorette_1.bold)(service)}...`);
|
|
24
24
|
try {
|
|
25
|
-
const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-user-project":
|
|
25
|
+
const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-user-project": `${projectNumber}` } });
|
|
26
26
|
return res.body;
|
|
27
27
|
}
|
|
28
28
|
catch (err) {
|
|
@@ -39,6 +39,6 @@ async function generateServiceIdentityAndPoll(projectNumber, service, prefix) {
|
|
|
39
39
|
await poller.pollOperation({
|
|
40
40
|
...serviceUsagePollerOptions,
|
|
41
41
|
operationResourceName: op.name,
|
|
42
|
-
headers: { "x-goog-user-project":
|
|
42
|
+
headers: { "x-goog-user-project": `${projectNumber}` },
|
|
43
43
|
});
|
|
44
44
|
}
|
|
@@ -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 = {
|