firebase-tools 9.20.0 → 9.21.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/CHANGELOG.md +3 -1
- package/lib/apiv2.js +4 -2
- package/lib/commands/crashlytics-symbols-upload.js +1 -1
- package/lib/commands/ext-dev-unpublish.js +10 -3
- package/lib/commands/functions-delete.js +53 -42
- package/lib/commands/functions-list.js +11 -11
- package/lib/deploy/functions/backend.js +77 -115
- package/lib/deploy/functions/checkIam.js +8 -8
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +61 -29
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +22 -21
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +362 -0
- package/lib/deploy/functions/release/index.js +69 -0
- package/lib/deploy/functions/release/planner.js +159 -0
- package/lib/deploy/functions/release/reporter.js +162 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +102 -127
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +22 -43
- package/lib/deploy/functions/triggerRegionHelper.js +28 -20
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/emulator/auth/apiSpec.js +37 -6
- package/lib/emulator/auth/operations.js +18 -8
- package/lib/emulator/auth/server.js +16 -2
- package/lib/emulator/auth/state.js +34 -15
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +15 -3
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/extensions/extensionsHelper.js +1 -1
- package/lib/gcp/cloudfunctions.js +1 -68
- package/lib/gcp/cloudfunctionsv2.js +2 -94
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/pubsub.js +1 -9
- package/lib/utils.js +30 -1
- package/package.json +1 -1
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functions/listFunctions.js +0 -10
- package/lib/functionsDelete.js +0 -60
|
@@ -3580,6 +3580,37 @@ exports.default = {
|
|
|
3580
3580
|
tags: ["emulator"],
|
|
3581
3581
|
},
|
|
3582
3582
|
},
|
|
3583
|
+
"/emulator/v1/projects/{targetProjectId}/tenants/{tenantId}/accounts": {
|
|
3584
|
+
parameters: [
|
|
3585
|
+
{
|
|
3586
|
+
name: "targetProjectId",
|
|
3587
|
+
in: "path",
|
|
3588
|
+
description: "The ID of the Google Cloud project that the accounts belong to.",
|
|
3589
|
+
required: true,
|
|
3590
|
+
schema: { type: "string" },
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
name: "tenantId",
|
|
3594
|
+
in: "path",
|
|
3595
|
+
description: "The ID of the Identity Platform tenant the accounts belongs to. If not specified, accounts on the Identity Platform project are returned.",
|
|
3596
|
+
required: true,
|
|
3597
|
+
schema: { type: "string" },
|
|
3598
|
+
},
|
|
3599
|
+
],
|
|
3600
|
+
servers: [{ url: "" }],
|
|
3601
|
+
delete: {
|
|
3602
|
+
description: "Remove all accounts in the project, regardless of state.",
|
|
3603
|
+
operationId: "emulator.projects.accounts.delete",
|
|
3604
|
+
responses: {
|
|
3605
|
+
200: {
|
|
3606
|
+
description: "Successful response",
|
|
3607
|
+
content: { "application/json": { schema: { type: "object" } } },
|
|
3608
|
+
},
|
|
3609
|
+
},
|
|
3610
|
+
security: [],
|
|
3611
|
+
tags: ["emulator"],
|
|
3612
|
+
},
|
|
3613
|
+
},
|
|
3583
3614
|
"/emulator/v1/projects/{targetProjectId}/config": {
|
|
3584
3615
|
parameters: [
|
|
3585
3616
|
{
|
|
@@ -4978,7 +5009,7 @@ exports.default = {
|
|
|
4978
5009
|
type: "string",
|
|
4979
5010
|
},
|
|
4980
5011
|
postBody: {
|
|
4981
|
-
description: "If the user is signing in with an authorization response obtained via a previous CreateAuthUri authorization request, this is the body of the HTTP POST callback from the IdP, if present. Otherwise, if the user is signing in with a manually provided IdP credential, this should be a URL-encoded form that contains the credential (e.g. an ID token or access token for OAuth 2.0 IdPs) and the provider ID of the IdP that issued the credential. For example, if the user is signing in to the Google provider using a Google ID token, this should be set to `id_token=[GOOGLE_ID_TOKEN]&providerId=google.com`, where `[GOOGLE_ID_TOKEN]` should be replaced with the Google ID token. If the user is signing in to the Facebook provider using a Facebook access token, this should be set to `access_token=[FACEBOOK_ACCESS_TOKEN]&providerId=facebook.com`, where `[FACEBOOK_ACCESS_TOKEN]` should be replaced with the Facebook access token. If the user is signing in to the Twitter provider using a Twitter OAuth 1.0 credential, this should be set to `access_token=[TWITTER_ACCESS_TOKEN]&oauth_token_secret=[TWITTER_TOKEN_SECRET]&providerId=twitter.com`, where `[TWITTER_ACCESS_TOKEN]` and `[TWITTER_TOKEN_SECRET]` should be replaced with the Twitter OAuth access token and Twitter OAuth token secret respectively.",
|
|
5012
|
+
description: "If the user is signing in with an authorization response obtained via a previous CreateAuthUri authorization request, this is the body of the HTTP POST callback from the IdP, if present. Otherwise, if the user is signing in with a manually provided IdP credential, this should be a URL-encoded form that contains the credential (e.g. an ID token or access token for OAuth 2.0 IdPs) and the provider ID of the IdP that issued the credential. For example, if the user is signing in to the Google provider using a Google ID token, this should be set to `id_token=[GOOGLE_ID_TOKEN]&providerId=google.com`, where `[GOOGLE_ID_TOKEN]` should be replaced with the Google ID token. If the user is signing in to the Facebook provider using a Facebook authentication token, this should be set to `id_token=[FACEBOOK_AUTHENTICATION_TOKEN]&providerId=facebook.com&nonce= [NONCE]`, where `[FACEBOOK_AUTHENTICATION_TOKEN]` should be replaced with the Facebook authentication token. Nonce is required for validating the token. The request will fail if no nonce is provided. If the user is signing in to the Facebook provider using a Facebook access token, this should be set to `access_token=[FACEBOOK_ACCESS_TOKEN]&providerId=facebook.com`, where `[FACEBOOK_ACCESS_TOKEN]` should be replaced with the Facebook access token. If the user is signing in to the Twitter provider using a Twitter OAuth 1.0 credential, this should be set to `access_token=[TWITTER_ACCESS_TOKEN]&oauth_token_secret=[TWITTER_TOKEN_SECRET]&providerId=twitter.com`, where `[TWITTER_ACCESS_TOKEN]` and `[TWITTER_TOKEN_SECRET]` should be replaced with the Twitter OAuth access token and Twitter OAuth token secret respectively.",
|
|
4982
5013
|
type: "string",
|
|
4983
5014
|
},
|
|
4984
5015
|
requestUri: {
|
|
@@ -6665,16 +6696,16 @@ exports.default = {
|
|
|
6665
6696
|
type: "object",
|
|
6666
6697
|
},
|
|
6667
6698
|
GoogleIamV1Binding: {
|
|
6668
|
-
description: "Associates `members
|
|
6699
|
+
description: "Associates `members`, or principals, with a `role`.",
|
|
6669
6700
|
properties: {
|
|
6670
6701
|
condition: { $ref: "#/components/schemas/GoogleTypeExpr" },
|
|
6671
6702
|
members: {
|
|
6672
|
-
description: "Specifies the
|
|
6703
|
+
description: "Specifies the principals requesting access for a Cloud Platform resource. `members` can have the following values: * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google account. * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google account or a service account. * `user:{emailid}`: An email address that represents a specific Google account. For example, `alice@example.com` . * `serviceAccount:{emailid}`: An email address that represents a service account. For example, `my-other-app@appspot.gserviceaccount.com`. * `group:{emailid}`: An email address that represents a Google group. For example, `admins@example.com`. * `deleted:user:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a user that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user is recovered, this value reverts to `user:{emailid}` and the recovered user retains the role in the binding. * `deleted:serviceAccount:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a service account that has been recently deleted. For example, `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted, this value reverts to `serviceAccount:{emailid}` and the undeleted service account retains the role in the binding. * `deleted:group:{emailid}?uid={uniqueid}`: An email address (plus unique identifier) representing a Google group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the group is recovered, this value reverts to `group:{emailid}` and the recovered group retains the role in the binding. * `domain:{domain}`: The G Suite domain (primary) that represents all the users of that domain. For example, `google.com` or `example.com`. ",
|
|
6673
6704
|
items: { type: "string" },
|
|
6674
6705
|
type: "array",
|
|
6675
6706
|
},
|
|
6676
6707
|
role: {
|
|
6677
|
-
description: "Role that is assigned to `members
|
|
6708
|
+
description: "Role that is assigned to the list of `members`, or principals. For example, `roles/viewer`, `roles/editor`, or `roles/owner`.",
|
|
6678
6709
|
type: "string",
|
|
6679
6710
|
},
|
|
6680
6711
|
},
|
|
@@ -6697,7 +6728,7 @@ exports.default = {
|
|
|
6697
6728
|
type: "object",
|
|
6698
6729
|
},
|
|
6699
6730
|
GoogleIamV1Policy: {
|
|
6700
|
-
description: 'An Identity and Access Management (IAM) policy, which specifies access controls for Google Cloud resources. A `Policy` is a collection of `bindings`. A `binding` binds one or more `members
|
|
6731
|
+
description: 'An Identity and Access Management (IAM) policy, which specifies access controls for Google Cloud resources. A `Policy` is a collection of `bindings`. A `binding` binds one or more `members`, or principals, to a single `role`. Principals can be user accounts, service accounts, Google groups, and domains (such as G Suite). A `role` is a named list of permissions; each `role` can be an IAM predefined role or a user-created custom role. For some types of Google Cloud resources, a `binding` can also specify a `condition`, which is a logical expression that allows access to a resource only if the expression evaluates to `true`. A condition can add constraints based on attributes of the request, the resource, or both. To learn which resources support conditions in their IAM policies, see the [IAM documentation](https://cloud.google.com/iam/help/conditions/resource-policies). **JSON example:** { "bindings": [ { "role": "roles/resourcemanager.organizationAdmin", "members": [ "user:mike@example.com", "group:admins@example.com", "domain:google.com", "serviceAccount:my-project-id@appspot.gserviceaccount.com" ] }, { "role": "roles/resourcemanager.organizationViewer", "members": [ "user:eve@example.com" ], "condition": { "title": "expirable access", "description": "Does not grant access after Sep 2020", "expression": "request.time < timestamp(\'2020-10-01T00:00:00.000Z\')", } } ], "etag": "BwWWja0YfJA=", "version": 3 } **YAML example:** bindings: - members: - user:mike@example.com - group:admins@example.com - domain:google.com - serviceAccount:my-project-id@appspot.gserviceaccount.com role: roles/resourcemanager.organizationAdmin - members: - user:eve@example.com role: roles/resourcemanager.organizationViewer condition: title: expirable access description: Does not grant access after Sep 2020 expression: request.time < timestamp(\'2020-10-01T00:00:00.000Z\') etag: BwWWja0YfJA= version: 3 For a description of IAM and its features, see the [IAM documentation](https://cloud.google.com/iam/docs/).',
|
|
6701
6732
|
properties: {
|
|
6702
6733
|
auditConfigs: {
|
|
6703
6734
|
description: "Specifies cloud audit logging configuration for this policy.",
|
|
@@ -6705,7 +6736,7 @@ exports.default = {
|
|
|
6705
6736
|
type: "array",
|
|
6706
6737
|
},
|
|
6707
6738
|
bindings: {
|
|
6708
|
-
description: "Associates a list of `members
|
|
6739
|
+
description: "Associates a list of `members`, or principals, with a `role`. Optionally, may specify a `condition` that determines how and when the `bindings` are applied. Each of the `bindings` must contain at least one principal. The `bindings` in a `Policy` can refer to up to 1,500 principals; up to 250 of these principals can be Google groups. Each occurrence of a principal counts towards these limits. For example, if the `bindings` grant 50 different roles to `user:alice@example.com`, and not to any other principal, then you can add another 1,450 principals to the `bindings` in the `Policy`.",
|
|
6709
6740
|
items: { $ref: "#/components/schemas/GoogleIamV1Binding" },
|
|
6710
6741
|
type: "array",
|
|
6711
6742
|
},
|
|
@@ -160,8 +160,8 @@ function signUp(state, reqBody, ctx) {
|
|
|
160
160
|
generateEnrollmentIds: true,
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
|
-
if (
|
|
164
|
-
updates.tenantId =
|
|
163
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
164
|
+
updates.tenantId = state.tenantId;
|
|
165
165
|
}
|
|
166
166
|
let user;
|
|
167
167
|
if (reqBody.idToken) {
|
|
@@ -267,6 +267,8 @@ function batchCreate(state, reqBody) {
|
|
|
267
267
|
};
|
|
268
268
|
if (userInfo.tenantId) {
|
|
269
269
|
errors_1.assert(state instanceof state_1.TenantProjectState && state.tenantId === userInfo.tenantId, "Tenant id in userInfo does not match the tenant id in request.");
|
|
270
|
+
}
|
|
271
|
+
if (state instanceof state_1.TenantProjectState) {
|
|
270
272
|
fields.tenantId = state.tenantId;
|
|
271
273
|
}
|
|
272
274
|
if (userInfo.passwordHash) {
|
|
@@ -921,7 +923,7 @@ function signInWithCustomToken(state, reqBody) {
|
|
|
921
923
|
else {
|
|
922
924
|
const decoded = jsonwebtoken_1.decode(reqBody.token, { complete: true });
|
|
923
925
|
if (state instanceof state_1.TenantProjectState) {
|
|
924
|
-
errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.
|
|
926
|
+
errors_1.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.tenant_id) === state.tenantId, "TENANT_ID_MISMATCH");
|
|
925
927
|
}
|
|
926
928
|
errors_1.assert(decoded, "INVALID_CUSTOM_TOKEN : Invalid assertion format");
|
|
927
929
|
if (decoded.header.alg !== "none") {
|
|
@@ -1798,16 +1800,24 @@ function parsePendingCredential(state, pendingCredential) {
|
|
|
1798
1800
|
return { user, signInProvider };
|
|
1799
1801
|
}
|
|
1800
1802
|
function createTenant(state, reqBody) {
|
|
1803
|
+
var _a, _b, _c, _d, _e;
|
|
1801
1804
|
if (!(state instanceof state_1.AgentProjectState)) {
|
|
1802
1805
|
throw new errors_1.InternalError("INTERNAL_ERROR: Can only create tenant in agent project", "INTERNAL");
|
|
1803
1806
|
}
|
|
1807
|
+
const mfaConfig = (_a = reqBody.mfaConfig) !== null && _a !== void 0 ? _a : {};
|
|
1808
|
+
if (!("state" in mfaConfig)) {
|
|
1809
|
+
mfaConfig.state = "DISABLED";
|
|
1810
|
+
}
|
|
1811
|
+
if (!("enabledProviders" in mfaConfig)) {
|
|
1812
|
+
mfaConfig.enabledProviders = [];
|
|
1813
|
+
}
|
|
1804
1814
|
const tenant = {
|
|
1805
1815
|
displayName: reqBody.displayName,
|
|
1806
|
-
allowPasswordSignup: reqBody.allowPasswordSignup,
|
|
1807
|
-
enableEmailLinkSignin: reqBody.enableEmailLinkSignin,
|
|
1808
|
-
enableAnonymousUser: reqBody.enableAnonymousUser,
|
|
1809
|
-
disableAuth: reqBody.disableAuth,
|
|
1810
|
-
mfaConfig:
|
|
1816
|
+
allowPasswordSignup: (_b = reqBody.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
|
|
1817
|
+
enableEmailLinkSignin: (_c = reqBody.enableEmailLinkSignin) !== null && _c !== void 0 ? _c : false,
|
|
1818
|
+
enableAnonymousUser: (_d = reqBody.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
|
|
1819
|
+
disableAuth: (_e = reqBody.disableAuth) !== null && _e !== void 0 ? _e : false,
|
|
1820
|
+
mfaConfig: mfaConfig,
|
|
1811
1821
|
tenantId: "",
|
|
1812
1822
|
};
|
|
1813
1823
|
return state.createTenant(tenant);
|
|
@@ -17,6 +17,7 @@ const lodash_1 = require("lodash");
|
|
|
17
17
|
const handlers_1 = require("./handlers");
|
|
18
18
|
const bodyParser = require("body-parser");
|
|
19
19
|
const url_1 = require("url");
|
|
20
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
20
21
|
const apiSpec = apiSpec_1.default;
|
|
21
22
|
const API_SPEC_PATH = "/emulator/openapi.json";
|
|
22
23
|
const AUTH_HEADER_PREFIX = "bearer ";
|
|
@@ -70,6 +71,10 @@ async function createApp(defaultProjectId, projectStateForId = new Map()) {
|
|
|
70
71
|
const app = express();
|
|
71
72
|
app.set("json spaces", 2);
|
|
72
73
|
app.use(cors({ origin: true }));
|
|
74
|
+
app.delete("*", (req, _, next) => {
|
|
75
|
+
delete req.headers["content-type"];
|
|
76
|
+
next();
|
|
77
|
+
});
|
|
73
78
|
app.get("/", (req, res) => {
|
|
74
79
|
return res.json({
|
|
75
80
|
authEmulator: {
|
|
@@ -334,7 +339,7 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
334
339
|
}
|
|
335
340
|
function toExegesisOperation(operation) {
|
|
336
341
|
return (ctx) => {
|
|
337
|
-
var _a, _b, _c, _d, _e;
|
|
342
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
338
343
|
let targetProjectId = ctx.params.path.targetProjectId || ((_a = ctx.requestBody) === null || _a === void 0 ? void 0 : _a.targetProjectId);
|
|
339
344
|
if (targetProjectId) {
|
|
340
345
|
if ((_b = ctx.api.operationObject.security) === null || _b === void 0 ? void 0 : _b.some((sec) => sec.Oauth2)) {
|
|
@@ -344,10 +349,19 @@ function toExegesisController(ops, getProjectStateById) {
|
|
|
344
349
|
else {
|
|
345
350
|
targetProjectId = ctx.user;
|
|
346
351
|
}
|
|
352
|
+
let targetTenantId = undefined;
|
|
347
353
|
if (ctx.params.path.tenantId && ((_d = ctx.requestBody) === null || _d === void 0 ? void 0 : _d.tenantId)) {
|
|
348
354
|
errors_2.assert(ctx.params.path.tenantId === ctx.requestBody.tenantId, "TENANT_ID_MISMATCH");
|
|
349
355
|
}
|
|
350
|
-
|
|
356
|
+
targetTenantId = ctx.params.path.tenantId || ((_e = ctx.requestBody) === null || _e === void 0 ? void 0 : _e.tenantId);
|
|
357
|
+
if ((_f = ctx.requestBody) === null || _f === void 0 ? void 0 : _f.idToken) {
|
|
358
|
+
const idToken = (_g = ctx.requestBody) === null || _g === void 0 ? void 0 : _g.idToken;
|
|
359
|
+
const decoded = jsonwebtoken_1.decode(idToken, { complete: true });
|
|
360
|
+
if ((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) && targetTenantId) {
|
|
361
|
+
errors_2.assert((decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant) === targetTenantId, "TENANT_ID_MISMATCH");
|
|
362
|
+
}
|
|
363
|
+
targetTenantId = targetTenantId || (decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant);
|
|
364
|
+
}
|
|
351
365
|
return operation(getProjectStateById(targetProjectId, targetTenantId), ctx.requestBody, ctx);
|
|
352
366
|
};
|
|
353
367
|
}
|
|
@@ -459,7 +459,17 @@ class AgentProjectState extends ProjectState {
|
|
|
459
459
|
}
|
|
460
460
|
getTenantProject(tenantId) {
|
|
461
461
|
if (!this.tenantProjectForTenantId.has(tenantId)) {
|
|
462
|
-
this.createTenantWithTenantId(tenantId, {
|
|
462
|
+
this.createTenantWithTenantId(tenantId, {
|
|
463
|
+
tenantId,
|
|
464
|
+
allowPasswordSignup: true,
|
|
465
|
+
disableAuth: false,
|
|
466
|
+
mfaConfig: {
|
|
467
|
+
state: "ENABLED",
|
|
468
|
+
enabledProviders: ["PHONE_SMS"],
|
|
469
|
+
},
|
|
470
|
+
enableAnonymousUser: true,
|
|
471
|
+
enableEmailLinkSignin: true,
|
|
472
|
+
});
|
|
463
473
|
}
|
|
464
474
|
return this.tenantProjectForTenantId.get(tenantId);
|
|
465
475
|
}
|
|
@@ -525,34 +535,43 @@ class TenantProjectState extends ProjectState {
|
|
|
525
535
|
return this._tenantConfig;
|
|
526
536
|
}
|
|
527
537
|
get allowPasswordSignup() {
|
|
528
|
-
|
|
529
|
-
return (_a = this._tenantConfig.allowPasswordSignup) !== null && _a !== void 0 ? _a : true;
|
|
538
|
+
return this._tenantConfig.allowPasswordSignup;
|
|
530
539
|
}
|
|
531
540
|
get disableAuth() {
|
|
532
|
-
|
|
533
|
-
return (_a = this._tenantConfig.disableAuth) !== null && _a !== void 0 ? _a : false;
|
|
541
|
+
return this._tenantConfig.disableAuth;
|
|
534
542
|
}
|
|
535
543
|
get mfaConfig() {
|
|
536
|
-
|
|
537
|
-
return ((_a = this._tenantConfig.mfaConfig) !== null && _a !== void 0 ? _a : {
|
|
538
|
-
state: "ENABLED",
|
|
539
|
-
enabledProviders: ["PHONE_SMS"],
|
|
540
|
-
});
|
|
544
|
+
return this._tenantConfig.mfaConfig;
|
|
541
545
|
}
|
|
542
546
|
get enableAnonymousUser() {
|
|
543
|
-
|
|
544
|
-
return (_a = this._tenantConfig.enableAnonymousUser) !== null && _a !== void 0 ? _a : true;
|
|
547
|
+
return this._tenantConfig.enableAnonymousUser;
|
|
545
548
|
}
|
|
546
549
|
get enableEmailLinkSignin() {
|
|
547
|
-
|
|
548
|
-
return (_a = this._tenantConfig.enableEmailLinkSignin) !== null && _a !== void 0 ? _a : true;
|
|
550
|
+
return this._tenantConfig.enableEmailLinkSignin;
|
|
549
551
|
}
|
|
550
552
|
delete() {
|
|
551
553
|
this.parentProject.deleteTenant(this.tenantId);
|
|
552
554
|
}
|
|
553
555
|
updateTenant(update, updateMask) {
|
|
556
|
+
var _a, _b, _c, _d, _e;
|
|
554
557
|
if (!updateMask) {
|
|
555
|
-
|
|
558
|
+
const mfaConfig = (_a = update.mfaConfig) !== null && _a !== void 0 ? _a : {};
|
|
559
|
+
if (!("state" in mfaConfig)) {
|
|
560
|
+
mfaConfig.state = "DISABLED";
|
|
561
|
+
}
|
|
562
|
+
if (!("enabledProviders" in mfaConfig)) {
|
|
563
|
+
mfaConfig.enabledProviders = [];
|
|
564
|
+
}
|
|
565
|
+
this._tenantConfig = {
|
|
566
|
+
tenantId: this.tenantId,
|
|
567
|
+
name: this.tenantConfig.name,
|
|
568
|
+
allowPasswordSignup: (_b = update.allowPasswordSignup) !== null && _b !== void 0 ? _b : false,
|
|
569
|
+
disableAuth: (_c = update.disableAuth) !== null && _c !== void 0 ? _c : false,
|
|
570
|
+
mfaConfig: mfaConfig,
|
|
571
|
+
enableAnonymousUser: (_d = update.enableAnonymousUser) !== null && _d !== void 0 ? _d : false,
|
|
572
|
+
enableEmailLinkSignin: (_e = update.enableEmailLinkSignin) !== null && _e !== void 0 ? _e : false,
|
|
573
|
+
displayName: update.displayName,
|
|
574
|
+
};
|
|
556
575
|
return this.tenantConfig;
|
|
557
576
|
}
|
|
558
577
|
const paths = updateMask.split(",");
|
|
@@ -50,15 +50,15 @@ exports.DownloadDetails = {
|
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
ui: {
|
|
53
|
-
version: "1.6.
|
|
54
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
55
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.6.
|
|
56
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.6.
|
|
53
|
+
version: "1.6.4",
|
|
54
|
+
downloadPath: path.join(CACHE_DIR, "ui-v1.6.4.zip"),
|
|
55
|
+
unzipDir: path.join(CACHE_DIR, "ui-v1.6.4"),
|
|
56
|
+
binaryPath: path.join(CACHE_DIR, "ui-v1.6.4", "server.bundle.js"),
|
|
57
57
|
opts: {
|
|
58
58
|
cacheDir: CACHE_DIR,
|
|
59
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.
|
|
60
|
-
expectedSize:
|
|
61
|
-
expectedChecksum: "
|
|
59
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.4.zip",
|
|
60
|
+
expectedSize: 3757300,
|
|
61
|
+
expectedChecksum: "20d4ee71e4ff7527b1843b6a8636142e",
|
|
62
62
|
namePrefix: "ui",
|
|
63
63
|
},
|
|
64
64
|
},
|
|
@@ -123,10 +123,22 @@ class FunctionsEmulator {
|
|
|
123
123
|
});
|
|
124
124
|
};
|
|
125
125
|
const multicastHandler = (req, res) => {
|
|
126
|
-
|
|
127
|
-
const proto = JSON.parse(reqBody.toString());
|
|
128
|
-
const triggers = this.multicastTriggers[`${this.args.projectId}:${proto.eventType}`] || [];
|
|
126
|
+
var _a;
|
|
129
127
|
const projectId = req.params.project_id;
|
|
128
|
+
const reqBody = req.rawBody;
|
|
129
|
+
let proto = JSON.parse(reqBody.toString());
|
|
130
|
+
let triggerKey;
|
|
131
|
+
if ((_a = req.headers["content-type"]) === null || _a === void 0 ? void 0 : _a.includes("cloudevent")) {
|
|
132
|
+
triggerKey = `${this.args.projectId}:${proto.type}`;
|
|
133
|
+
if (types_2.EventUtils.isBinaryCloudEvent(req)) {
|
|
134
|
+
proto = types_2.EventUtils.extractBinaryCloudEventContext(req);
|
|
135
|
+
proto.data = req.body;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
triggerKey = `${this.args.projectId}:${proto.eventType}`;
|
|
140
|
+
}
|
|
141
|
+
const triggers = this.multicastTriggers[triggerKey] || [];
|
|
130
142
|
triggers.forEach((triggerId) => {
|
|
131
143
|
this.workQueue.submit(() => {
|
|
132
144
|
this.logger.log("DEBUG", `Accepted multicast request ${req.method} ${req.url} --> ${triggerId}`);
|
|
@@ -6,6 +6,12 @@ const types_1 = require("../types");
|
|
|
6
6
|
const emulatorLogger_1 = require("../emulatorLogger");
|
|
7
7
|
const metadata_1 = require("./metadata");
|
|
8
8
|
const apiv2_1 = require("../../apiv2");
|
|
9
|
+
const STORAGE_V2_ACTION_MAP = {
|
|
10
|
+
finalize: "finalized",
|
|
11
|
+
metadataUpdate: "metadataUpdated",
|
|
12
|
+
delete: "deleted",
|
|
13
|
+
archive: "archived",
|
|
14
|
+
};
|
|
9
15
|
class StorageCloudFunctions {
|
|
10
16
|
constructor(projectId) {
|
|
11
17
|
this.projectId = projectId;
|
|
@@ -19,26 +25,37 @@ class StorageCloudFunctions {
|
|
|
19
25
|
this.functionsEmulatorInfo = functionsEmulator.getInfo();
|
|
20
26
|
this.multicastOrigin = `http://${registry_1.EmulatorRegistry.getInfoHostString(this.functionsEmulatorInfo)}`;
|
|
21
27
|
this.multicastPath = `/functions/projects/${projectId}/trigger_multicast`;
|
|
28
|
+
this.client = new apiv2_1.Client({ urlPrefix: this.multicastOrigin, auth: false });
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
async dispatch(action, object) {
|
|
25
|
-
if (!this.enabled)
|
|
32
|
+
if (!this.enabled) {
|
|
26
33
|
return;
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
let res;
|
|
34
|
+
}
|
|
35
|
+
const errStatus = [];
|
|
30
36
|
let err;
|
|
31
37
|
try {
|
|
32
|
-
|
|
38
|
+
const eventBody = this.createLegacyEventRequestBody(action, object);
|
|
39
|
+
const eventRes = await this.client.post(this.multicastPath, eventBody);
|
|
40
|
+
if (eventRes.status !== 200) {
|
|
41
|
+
errStatus.push(eventRes.status);
|
|
42
|
+
}
|
|
43
|
+
const cloudEventBody = this.createCloudEventRequestBody(action, object);
|
|
44
|
+
const cloudEventRes = await this.client.post(this.multicastPath, cloudEventBody, {
|
|
45
|
+
headers: { "Content-Type": "application/cloudevents+json; charset=UTF-8" },
|
|
46
|
+
});
|
|
47
|
+
if (cloudEventRes.status !== 200) {
|
|
48
|
+
errStatus.push(cloudEventRes.status);
|
|
49
|
+
}
|
|
33
50
|
}
|
|
34
51
|
catch (e) {
|
|
35
52
|
err = e;
|
|
36
53
|
}
|
|
37
|
-
if (err ||
|
|
54
|
+
if (err || errStatus.length > 0) {
|
|
38
55
|
this.logger.logLabeled("WARN", "functions", `Firebase Storage function was not triggered due to emulation error. Please file a bug.`);
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
|
-
|
|
58
|
+
createLegacyEventRequestBody(action, objectMetadataPayload) {
|
|
42
59
|
const timestamp = new Date();
|
|
43
60
|
return JSON.stringify({
|
|
44
61
|
eventId: `${timestamp.getTime()}`,
|
|
@@ -52,5 +69,18 @@ class StorageCloudFunctions {
|
|
|
52
69
|
data: objectMetadataPayload,
|
|
53
70
|
});
|
|
54
71
|
}
|
|
72
|
+
createCloudEventRequestBody(action, objectMetadataPayload) {
|
|
73
|
+
const ceAction = STORAGE_V2_ACTION_MAP[action];
|
|
74
|
+
if (!ceAction) {
|
|
75
|
+
throw new Error("Action is not definied as a CloudEvents action");
|
|
76
|
+
}
|
|
77
|
+
const data = objectMetadataPayload;
|
|
78
|
+
return JSON.stringify({
|
|
79
|
+
specVersion: 1,
|
|
80
|
+
type: `google.cloud.storage.object.v1.${ceAction}`,
|
|
81
|
+
source: `//storage.googleapis.com/projects/_/buckets/${objectMetadataPayload.bucket}/objects/${objectMetadataPayload.name}`,
|
|
82
|
+
data,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
55
85
|
}
|
|
56
86
|
exports.StorageCloudFunctions = StorageCloudFunctions;
|
|
@@ -307,7 +307,7 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
307
307
|
else if (extension &&
|
|
308
308
|
extension.latestVersion &&
|
|
309
309
|
semver.eq(extensionSpec.version, extension.latestVersion)) {
|
|
310
|
-
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) already exists for the extension '${clc.bold(`${args.publisherId}/${args.extensionId}`)}'. Please increment the version inside of extension.yaml.\n
|
|
310
|
+
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) already exists for the extension '${clc.bold(`${args.publisherId}/${args.extensionId}`)}'. Please increment the version inside of extension.yaml.\n`, { exit: 103 });
|
|
311
311
|
}
|
|
312
312
|
const ref = `${args.publisherId}/${args.extensionId}@${extensionSpec.version}`;
|
|
313
313
|
let packageUri;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.functionFromEndpoint = exports.endpointFromFunction = exports.
|
|
3
|
+
exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
@@ -214,73 +214,6 @@ async function listAllFunctions(projectId) {
|
|
|
214
214
|
return list(projectId, "-");
|
|
215
215
|
}
|
|
216
216
|
exports.listAllFunctions = listAllFunctions;
|
|
217
|
-
function specFromFunction(gcfFunction) {
|
|
218
|
-
var _a;
|
|
219
|
-
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
220
|
-
let trigger;
|
|
221
|
-
let uri;
|
|
222
|
-
if (gcfFunction.httpsTrigger) {
|
|
223
|
-
trigger = {};
|
|
224
|
-
uri = gcfFunction.httpsTrigger.url;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
trigger = {
|
|
228
|
-
eventType: gcfFunction.eventTrigger.eventType,
|
|
229
|
-
eventFilters: {
|
|
230
|
-
resource: gcfFunction.eventTrigger.resource,
|
|
231
|
-
},
|
|
232
|
-
retry: !!((_a = gcfFunction.eventTrigger.failurePolicy) === null || _a === void 0 ? void 0 : _a.retry),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
|
|
236
|
-
logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
|
|
237
|
-
}
|
|
238
|
-
const cloudFunction = {
|
|
239
|
-
platform: "gcfv1",
|
|
240
|
-
id,
|
|
241
|
-
project,
|
|
242
|
-
region,
|
|
243
|
-
trigger,
|
|
244
|
-
entryPoint: gcfFunction.entryPoint,
|
|
245
|
-
runtime: gcfFunction.runtime,
|
|
246
|
-
};
|
|
247
|
-
if (uri) {
|
|
248
|
-
cloudFunction.uri = uri;
|
|
249
|
-
}
|
|
250
|
-
proto.copyIfPresent(cloudFunction, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables", "sourceUploadUrl");
|
|
251
|
-
return cloudFunction;
|
|
252
|
-
}
|
|
253
|
-
exports.specFromFunction = specFromFunction;
|
|
254
|
-
function functionFromSpec(cloudFunction, sourceUploadUrl) {
|
|
255
|
-
if (cloudFunction.platform != "gcfv1") {
|
|
256
|
-
throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
|
|
257
|
-
}
|
|
258
|
-
if (!runtimes.isValidRuntime(cloudFunction.runtime)) {
|
|
259
|
-
throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
|
|
260
|
-
" This should never happen");
|
|
261
|
-
}
|
|
262
|
-
const gcfFunction = {
|
|
263
|
-
name: backend.functionName(cloudFunction),
|
|
264
|
-
sourceUploadUrl: sourceUploadUrl,
|
|
265
|
-
entryPoint: cloudFunction.entryPoint,
|
|
266
|
-
runtime: cloudFunction.runtime,
|
|
267
|
-
};
|
|
268
|
-
if (backend.isEventTrigger(cloudFunction.trigger)) {
|
|
269
|
-
gcfFunction.eventTrigger = {
|
|
270
|
-
eventType: cloudFunction.trigger.eventType,
|
|
271
|
-
resource: cloudFunction.trigger.eventFilters.resource,
|
|
272
|
-
};
|
|
273
|
-
gcfFunction.eventTrigger.failurePolicy = cloudFunction.trigger.retry
|
|
274
|
-
? { retry: {} }
|
|
275
|
-
: undefined;
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
gcfFunction.httpsTrigger = {};
|
|
279
|
-
}
|
|
280
|
-
proto.copyIfPresent(gcfFunction, cloudFunction, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables");
|
|
281
|
-
return gcfFunction;
|
|
282
|
-
}
|
|
283
|
-
exports.functionFromSpec = functionFromSpec;
|
|
284
217
|
function endpointFromFunction(gcfFunction) {
|
|
285
218
|
var _a, _b;
|
|
286
219
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.endpointFromFunction = exports.functionFromEndpoint = exports.
|
|
3
|
+
exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.PUBSUB_PUBLISH_EVENT = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const error_1 = require("../error");
|
|
@@ -115,99 +115,6 @@ async function deleteFunction(cloudFunction) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
exports.deleteFunction = deleteFunction;
|
|
118
|
-
function functionFromSpec(cloudFunction, source) {
|
|
119
|
-
if (cloudFunction.platform != "gcfv2") {
|
|
120
|
-
throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
|
|
121
|
-
}
|
|
122
|
-
if (!runtimes.isValidRuntime(cloudFunction.runtime)) {
|
|
123
|
-
throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
|
|
124
|
-
" This should never happen");
|
|
125
|
-
}
|
|
126
|
-
const gcfFunction = {
|
|
127
|
-
name: backend.functionName(cloudFunction),
|
|
128
|
-
buildConfig: {
|
|
129
|
-
runtime: cloudFunction.runtime,
|
|
130
|
-
entryPoint: cloudFunction.entryPoint,
|
|
131
|
-
source: {
|
|
132
|
-
storageSource: source,
|
|
133
|
-
},
|
|
134
|
-
environmentVariables: {},
|
|
135
|
-
},
|
|
136
|
-
serviceConfig: {},
|
|
137
|
-
};
|
|
138
|
-
proto.copyIfPresent(gcfFunction.serviceConfig, cloudFunction, "availableMemoryMb", "environmentVariables", "vpcConnector", "vpcConnectorEgressSettings", "serviceAccountEmail", "ingressSettings");
|
|
139
|
-
proto.renameIfPresent(gcfFunction.serviceConfig, cloudFunction, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
140
|
-
proto.renameIfPresent(gcfFunction.serviceConfig, cloudFunction, "minInstanceCount", "minInstances");
|
|
141
|
-
proto.renameIfPresent(gcfFunction.serviceConfig, cloudFunction, "maxInstanceCount", "maxInstances");
|
|
142
|
-
if (backend.isEventTrigger(cloudFunction.trigger)) {
|
|
143
|
-
gcfFunction.eventTrigger = {
|
|
144
|
-
eventType: cloudFunction.trigger.eventType,
|
|
145
|
-
};
|
|
146
|
-
if (cloudFunction.trigger.region) {
|
|
147
|
-
gcfFunction.eventTrigger.triggerRegion = cloudFunction.trigger.region;
|
|
148
|
-
}
|
|
149
|
-
if (gcfFunction.eventTrigger.eventType === exports.PUBSUB_PUBLISH_EVENT) {
|
|
150
|
-
gcfFunction.eventTrigger.pubsubTopic = cloudFunction.trigger.eventFilters.resource;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
gcfFunction.eventTrigger.eventFilters = [];
|
|
154
|
-
for (const [attribute, value] of Object.entries(cloudFunction.trigger.eventFilters)) {
|
|
155
|
-
gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (cloudFunction.trigger.retry) {
|
|
159
|
-
logger_1.logger.warn("Cannot set a retry policy on Cloud Function", cloudFunction.id);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
proto.copyIfPresent(gcfFunction, cloudFunction, "labels");
|
|
163
|
-
return gcfFunction;
|
|
164
|
-
}
|
|
165
|
-
exports.functionFromSpec = functionFromSpec;
|
|
166
|
-
function specFromFunction(gcfFunction) {
|
|
167
|
-
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
168
|
-
let trigger;
|
|
169
|
-
if (gcfFunction.eventTrigger) {
|
|
170
|
-
trigger = {
|
|
171
|
-
eventType: gcfFunction.eventTrigger.eventType,
|
|
172
|
-
eventFilters: {},
|
|
173
|
-
retry: false,
|
|
174
|
-
};
|
|
175
|
-
if (gcfFunction.eventTrigger.triggerRegion) {
|
|
176
|
-
trigger.region = gcfFunction.eventTrigger.triggerRegion;
|
|
177
|
-
}
|
|
178
|
-
if (gcfFunction.eventTrigger.pubsubTopic) {
|
|
179
|
-
trigger.eventFilters.resource = gcfFunction.eventTrigger.pubsubTopic;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
|
|
183
|
-
trigger.eventFilters[attribute] = value;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
trigger = {};
|
|
189
|
-
}
|
|
190
|
-
if (!runtimes.isValidRuntime(gcfFunction.buildConfig.runtime)) {
|
|
191
|
-
logger_1.logger.debug("GCFv2 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
|
|
192
|
-
}
|
|
193
|
-
const cloudFunction = {
|
|
194
|
-
platform: "gcfv2",
|
|
195
|
-
id,
|
|
196
|
-
project,
|
|
197
|
-
region,
|
|
198
|
-
trigger,
|
|
199
|
-
entryPoint: gcfFunction.buildConfig.entryPoint,
|
|
200
|
-
runtime: gcfFunction.buildConfig.runtime,
|
|
201
|
-
uri: gcfFunction.serviceConfig.uri,
|
|
202
|
-
};
|
|
203
|
-
proto.copyIfPresent(cloudFunction, gcfFunction.serviceConfig, "serviceAccountEmail", "availableMemoryMb", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
|
|
204
|
-
proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
|
|
205
|
-
proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
|
|
206
|
-
proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
|
|
207
|
-
proto.copyIfPresent(cloudFunction, gcfFunction, "labels");
|
|
208
|
-
return cloudFunction;
|
|
209
|
-
}
|
|
210
|
-
exports.specFromFunction = specFromFunction;
|
|
211
118
|
function functionFromEndpoint(endpoint, source) {
|
|
212
119
|
if (endpoint.platform != "gcfv2") {
|
|
213
120
|
throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
|
|
@@ -281,6 +188,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
281
188
|
trigger.eventTrigger.eventFilters[attribute] = value;
|
|
282
189
|
}
|
|
283
190
|
}
|
|
191
|
+
proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
|
|
284
192
|
}
|
|
285
193
|
else {
|
|
286
194
|
trigger = { httpsTrigger: {} };
|