firebase-tools 9.18.0 → 9.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +3 -6
  2. package/lib/api.js +3 -0
  3. package/lib/apiv2.js +8 -5
  4. package/lib/command.js +1 -1
  5. package/lib/commands/crashlytics-symbols-upload.js +146 -0
  6. package/lib/commands/deploy.js +9 -1
  7. package/lib/commands/ext-configure.js +9 -2
  8. package/lib/commands/ext-dev-deprecate.js +63 -0
  9. package/lib/commands/ext-dev-extension-delete.js +2 -1
  10. package/lib/commands/ext-dev-publish.js +10 -4
  11. package/lib/commands/ext-dev-undeprecate.js +56 -0
  12. package/lib/commands/ext-dev-unpublish.js +12 -4
  13. package/lib/commands/ext-export.js +44 -0
  14. package/lib/commands/ext-install.js +50 -13
  15. package/lib/commands/ext-uninstall.js +6 -0
  16. package/lib/commands/ext-update.js +60 -18
  17. package/lib/commands/functions-config-export.js +115 -0
  18. package/lib/commands/functions-delete.js +47 -25
  19. package/lib/commands/functions-list.js +12 -12
  20. package/lib/commands/index.js +9 -0
  21. package/lib/commands/init.js +3 -0
  22. package/lib/config.js +3 -2
  23. package/lib/deploy/extensions/args.js +2 -0
  24. package/lib/deploy/extensions/deploy.js +49 -0
  25. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  26. package/lib/deploy/extensions/errors.js +31 -0
  27. package/lib/deploy/extensions/index.js +8 -0
  28. package/lib/deploy/extensions/planner.js +95 -0
  29. package/lib/deploy/extensions/prepare.js +103 -0
  30. package/lib/deploy/extensions/release.js +43 -0
  31. package/lib/deploy/extensions/secrets.js +150 -0
  32. package/lib/deploy/extensions/tasks.js +98 -0
  33. package/lib/deploy/extensions/validate.js +17 -0
  34. package/lib/deploy/functions/backend.js +93 -115
  35. package/lib/deploy/functions/checkIam.js +8 -8
  36. package/lib/deploy/functions/containerCleaner.js +82 -22
  37. package/lib/deploy/functions/deploy.js +4 -10
  38. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  39. package/lib/deploy/functions/prepare.js +62 -27
  40. package/lib/deploy/functions/pricing.js +17 -17
  41. package/lib/deploy/functions/prompts.js +22 -21
  42. package/lib/deploy/functions/release/executor.js +39 -0
  43. package/lib/deploy/functions/release/fabricator.js +422 -0
  44. package/lib/deploy/functions/release/index.js +73 -0
  45. package/lib/deploy/functions/release/planner.js +162 -0
  46. package/lib/deploy/functions/release/reporter.js +165 -0
  47. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  48. package/lib/deploy/functions/release/timer.js +14 -0
  49. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
  50. package/lib/deploy/functions/runtimes/node/parseTriggers.js +41 -45
  51. package/lib/deploy/functions/triggerRegionHelper.js +40 -0
  52. package/lib/deploy/functions/validate.js +1 -24
  53. package/lib/deploy/index.js +1 -0
  54. package/lib/downloadUtils.js +37 -0
  55. package/lib/emulator/auth/apiSpec.js +1788 -403
  56. package/lib/emulator/auth/handlers.js +6 -5
  57. package/lib/emulator/auth/operations.js +439 -40
  58. package/lib/emulator/auth/server.js +32 -11
  59. package/lib/emulator/auth/state.js +205 -5
  60. package/lib/emulator/auth/widget_ui.js +2 -2
  61. package/lib/emulator/download.js +2 -31
  62. package/lib/emulator/downloadableEmulators.js +7 -7
  63. package/lib/emulator/emulatorLogger.js +0 -3
  64. package/lib/emulator/events/types.js +16 -0
  65. package/lib/emulator/functionsEmulator.js +120 -21
  66. package/lib/emulator/functionsEmulatorRuntime.js +46 -121
  67. package/lib/emulator/functionsEmulatorShared.js +51 -7
  68. package/lib/emulator/functionsEmulatorShell.js +1 -1
  69. package/lib/emulator/pubsubEmulator.js +61 -40
  70. package/lib/emulator/storage/cloudFunctions.js +37 -7
  71. package/lib/extensions/askUserForConsent.js +14 -1
  72. package/lib/extensions/askUserForParam.js +81 -4
  73. package/lib/extensions/billingMigrationHelper.js +1 -11
  74. package/lib/extensions/changelog.js +2 -1
  75. package/lib/extensions/checkProjectBilling.js +7 -7
  76. package/lib/extensions/displayExtensionInfo.js +35 -33
  77. package/lib/extensions/emulator/optionsHelper.js +3 -3
  78. package/lib/extensions/emulator/triggerHelper.js +2 -32
  79. package/lib/extensions/export.js +107 -0
  80. package/lib/extensions/extensionsApi.js +149 -97
  81. package/lib/extensions/extensionsHelper.js +36 -32
  82. package/lib/extensions/listExtensions.js +16 -11
  83. package/lib/extensions/paramHelper.js +73 -40
  84. package/lib/extensions/provisioningHelper.js +16 -3
  85. package/lib/extensions/refs.js +67 -0
  86. package/lib/extensions/secretsUtils.js +59 -0
  87. package/lib/extensions/updateHelper.js +33 -47
  88. package/lib/extensions/versionHelper.js +14 -0
  89. package/lib/extensions/warnings.js +33 -1
  90. package/lib/functional.js +64 -0
  91. package/lib/functions/env.js +26 -13
  92. package/lib/functions/runtimeConfigExport.js +137 -0
  93. package/lib/gcp/artifactregistry.js +16 -0
  94. package/lib/gcp/cloudfunctions.js +65 -35
  95. package/lib/gcp/cloudfunctionsv2.js +56 -43
  96. package/lib/gcp/cloudscheduler.js +22 -16
  97. package/lib/gcp/cloudtasks.js +143 -0
  98. package/lib/gcp/docker.js +7 -1
  99. package/lib/gcp/proto.js +2 -2
  100. package/lib/gcp/pubsub.js +1 -9
  101. package/lib/gcp/secretManager.js +132 -0
  102. package/lib/gcp/storage.js +16 -0
  103. package/lib/previews.js +1 -1
  104. package/lib/requireInteractive.js +12 -0
  105. package/lib/utils.js +30 -1
  106. package/package.json +6 -4
  107. package/schema/firebase-config.json +9 -0
  108. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  109. package/lib/deploy/functions/deploymentTimer.js +0 -23
  110. package/lib/deploy/functions/errorHandler.js +0 -75
  111. package/lib/deploy/functions/release.js +0 -116
  112. package/lib/deploy/functions/tasks.js +0 -324
  113. package/lib/functions/listFunctions.js +0 -10
  114. package/lib/functionsDelete.js +0 -60
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.functionFromSpec = exports.specFromFunction = 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;
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");
7
+ const previews_1 = require("../previews");
7
8
  const api = require("../api");
8
9
  const backend = require("../deploy/functions/backend");
9
10
  const utils = require("../utils");
@@ -50,7 +51,11 @@ async function createFunction(cloudFunction) {
50
51
  const apiPath = cloudFunction.name.substring(0, cloudFunction.name.lastIndexOf("/"));
51
52
  const endpoint = `/${exports.API_VERSION}/${apiPath}`;
52
53
  try {
54
+ const headers = previews_1.previews.artifactregistry
55
+ ? { "X-Firebase-Artifact-Registry": "optin" }
56
+ : undefined;
53
57
  const res = await api.request("POST", endpoint, {
58
+ headers,
54
59
  auth: true,
55
60
  data: cloudFunction,
56
61
  origin: api.functionsOrigin,
@@ -145,7 +150,11 @@ async function updateFunction(cloudFunction) {
145
150
  const endpoint = `/${exports.API_VERSION}/${cloudFunction.name}`;
146
151
  const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
147
152
  try {
153
+ const headers = previews_1.previews.artifactregistry
154
+ ? { "X-Firebase-Artifact-Registry": "optin" }
155
+ : undefined;
148
156
  const res = await api.request("PATCH", endpoint, {
157
+ headers,
149
158
  qs: {
150
159
  updateMask: fieldMasks.join(","),
151
160
  },
@@ -198,9 +207,11 @@ async function list(projectId, region) {
198
207
  };
199
208
  }
200
209
  catch (err) {
201
- logger_1.logger.debug("[functions] failed to list functions for " + projectId);
210
+ logger_1.logger.debug(`[functions] failed to list functions for ${projectId}`);
202
211
  logger_1.logger.debug(`[functions] ${err === null || err === void 0 ? void 0 : err.message}`);
203
- return Promise.reject(err === null || err === void 0 ? void 0 : err.message);
212
+ throw new error_1.FirebaseError(`Failed to list functions for ${projectId}`, {
213
+ original: err,
214
+ });
204
215
  }
205
216
  }
206
217
  async function listFunctions(projectId, region) {
@@ -212,70 +223,89 @@ async function listAllFunctions(projectId) {
212
223
  return list(projectId, "-");
213
224
  }
214
225
  exports.listAllFunctions = listAllFunctions;
215
- function specFromFunction(gcfFunction) {
216
- var _a;
226
+ function endpointFromFunction(gcfFunction) {
227
+ var _a, _b, _c;
217
228
  const [, project, , region, , id] = gcfFunction.name.split("/");
218
229
  let trigger;
219
230
  let uri;
220
- if (gcfFunction.httpsTrigger) {
221
- trigger = {};
231
+ if ((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
232
+ trigger = {
233
+ scheduleTrigger: {},
234
+ };
235
+ }
236
+ else if ((_b = gcfFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-taskqueue"]) {
237
+ trigger = {
238
+ taskQueueTrigger: {},
239
+ };
240
+ }
241
+ else if (gcfFunction.httpsTrigger) {
242
+ trigger = { httpsTrigger: {} };
222
243
  uri = gcfFunction.httpsTrigger.url;
223
244
  }
224
245
  else {
225
246
  trigger = {
226
- eventType: gcfFunction.eventTrigger.eventType,
227
- eventFilters: {
228
- resource: gcfFunction.eventTrigger.resource,
247
+ eventTrigger: {
248
+ eventType: gcfFunction.eventTrigger.eventType,
249
+ eventFilters: {
250
+ resource: gcfFunction.eventTrigger.resource,
251
+ },
252
+ retry: !!((_c = gcfFunction.eventTrigger.failurePolicy) === null || _c === void 0 ? void 0 : _c.retry),
229
253
  },
230
- retry: !!((_a = gcfFunction.eventTrigger.failurePolicy) === null || _a === void 0 ? void 0 : _a.retry),
231
254
  };
232
255
  }
233
256
  if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
234
257
  logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
235
258
  }
236
- const cloudFunction = {
237
- platform: "gcfv1",
238
- id,
259
+ const endpoint = Object.assign(Object.assign({ platform: "gcfv1", id,
239
260
  project,
240
- region,
241
- trigger,
242
- entryPoint: gcfFunction.entryPoint,
243
- runtime: gcfFunction.runtime,
244
- };
261
+ region }, trigger), { entryPoint: gcfFunction.entryPoint, runtime: gcfFunction.runtime });
245
262
  if (uri) {
246
- cloudFunction.uri = uri;
263
+ endpoint.uri = uri;
247
264
  }
248
- proto.copyIfPresent(cloudFunction, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables", "sourceUploadUrl");
249
- return cloudFunction;
265
+ proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables", "sourceUploadUrl");
266
+ return endpoint;
250
267
  }
251
- exports.specFromFunction = specFromFunction;
252
- function functionFromSpec(cloudFunction, sourceUploadUrl) {
253
- if (cloudFunction.platform != "gcfv1") {
268
+ exports.endpointFromFunction = endpointFromFunction;
269
+ function functionFromEndpoint(endpoint, sourceUploadUrl) {
270
+ if (endpoint.platform != "gcfv1") {
254
271
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
255
272
  }
256
- if (!runtimes.isValidRuntime(cloudFunction.runtime)) {
273
+ if (!runtimes.isValidRuntime(endpoint.runtime)) {
257
274
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
258
275
  " This should never happen");
259
276
  }
260
277
  const gcfFunction = {
261
- name: backend.functionName(cloudFunction),
278
+ name: backend.functionName(endpoint),
262
279
  sourceUploadUrl: sourceUploadUrl,
263
- entryPoint: cloudFunction.entryPoint,
264
- runtime: cloudFunction.runtime,
280
+ entryPoint: endpoint.entryPoint,
281
+ runtime: endpoint.runtime,
265
282
  };
266
- if (backend.isEventTrigger(cloudFunction.trigger)) {
283
+ proto.copyIfPresent(gcfFunction, endpoint, "labels");
284
+ if (backend.isEventTriggered(endpoint)) {
267
285
  gcfFunction.eventTrigger = {
268
- eventType: cloudFunction.trigger.eventType,
269
- resource: cloudFunction.trigger.eventFilters.resource,
286
+ eventType: endpoint.eventTrigger.eventType,
287
+ resource: endpoint.eventTrigger.eventFilters.resource,
270
288
  };
271
- gcfFunction.eventTrigger.failurePolicy = cloudFunction.trigger.retry
289
+ gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
272
290
  ? { retry: {} }
273
291
  : undefined;
274
292
  }
293
+ else if (backend.isScheduleTriggered(endpoint)) {
294
+ const id = backend.scheduleIdForFunction(endpoint);
295
+ gcfFunction.eventTrigger = {
296
+ eventType: "google.pubsub.topic.publish",
297
+ resource: `projects/${endpoint.project}/topics/${id}`,
298
+ };
299
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
300
+ }
301
+ else if (backend.isTaskQueueTriggered(endpoint)) {
302
+ gcfFunction.httpsTrigger = {};
303
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
304
+ }
275
305
  else {
276
306
  gcfFunction.httpsTrigger = {};
277
307
  }
278
- proto.copyIfPresent(gcfFunction, cloudFunction, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables");
308
+ proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
279
309
  return gcfFunction;
280
310
  }
281
- exports.functionFromSpec = functionFromSpec;
311
+ exports.functionFromEndpoint = functionFromEndpoint;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.specFromFunction = exports.functionFromSpec = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.PUBSUB_PUBLISH_EVENT = exports.API_VERSION = void 0;
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,19 +115,19 @@ async function deleteFunction(cloudFunction) {
115
115
  }
116
116
  }
117
117
  exports.deleteFunction = deleteFunction;
118
- function functionFromSpec(cloudFunction, source) {
119
- if (cloudFunction.platform != "gcfv2") {
118
+ function functionFromEndpoint(endpoint, source) {
119
+ if (endpoint.platform != "gcfv2") {
120
120
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
121
121
  }
122
- if (!runtimes.isValidRuntime(cloudFunction.runtime)) {
122
+ if (!runtimes.isValidRuntime(endpoint.runtime)) {
123
123
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
124
124
  " This should never happen");
125
125
  }
126
126
  const gcfFunction = {
127
- name: backend.functionName(cloudFunction),
127
+ name: backend.functionName(endpoint),
128
128
  buildConfig: {
129
- runtime: cloudFunction.runtime,
130
- entryPoint: cloudFunction.entryPoint,
129
+ runtime: endpoint.runtime,
130
+ entryPoint: endpoint.entryPoint,
131
131
  source: {
132
132
  storageSource: source,
133
133
  },
@@ -135,70 +135,83 @@ function functionFromSpec(cloudFunction, source) {
135
135
  },
136
136
  serviceConfig: {},
137
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)) {
138
+ proto.copyIfPresent(gcfFunction, endpoint, "labels");
139
+ proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemoryMb", "environmentVariables", "vpcConnector", "vpcConnectorEgressSettings", "serviceAccountEmail", "ingressSettings");
140
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "timeoutSeconds", "timeout", proto.secondsFromDuration);
141
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "minInstanceCount", "minInstances");
142
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "maxInstanceCount", "maxInstances");
143
+ if (backend.isEventTriggered(endpoint)) {
143
144
  gcfFunction.eventTrigger = {
144
- eventType: cloudFunction.trigger.eventType,
145
+ eventType: endpoint.eventTrigger.eventType,
145
146
  };
146
147
  if (gcfFunction.eventTrigger.eventType === exports.PUBSUB_PUBLISH_EVENT) {
147
- gcfFunction.eventTrigger.pubsubTopic = cloudFunction.trigger.eventFilters.resource;
148
+ gcfFunction.eventTrigger.pubsubTopic = endpoint.eventTrigger.eventFilters.resource;
148
149
  }
149
150
  else {
150
151
  gcfFunction.eventTrigger.eventFilters = [];
151
- for (const [attribute, value] of Object.entries(cloudFunction.trigger.eventFilters)) {
152
+ for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
152
153
  gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
153
154
  }
154
155
  }
155
- if (cloudFunction.trigger.retry) {
156
- logger_1.logger.warn("Cannot set a retry policy on Cloud Function", cloudFunction.id);
156
+ if (endpoint.eventTrigger.retry) {
157
+ logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
157
158
  }
158
159
  }
159
- proto.copyIfPresent(gcfFunction, cloudFunction, "labels");
160
+ else if (backend.isScheduleTriggered(endpoint)) {
161
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
162
+ }
163
+ else if (backend.isTaskQueueTriggered(endpoint)) {
164
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
165
+ }
160
166
  return gcfFunction;
161
167
  }
162
- exports.functionFromSpec = functionFromSpec;
163
- function specFromFunction(gcfFunction) {
168
+ exports.functionFromEndpoint = functionFromEndpoint;
169
+ function endpointFromFunction(gcfFunction) {
170
+ var _a, _b;
164
171
  const [, project, , region, , id] = gcfFunction.name.split("/");
165
172
  let trigger;
166
- if (gcfFunction.eventTrigger) {
173
+ if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
174
+ trigger = {
175
+ scheduleTrigger: {},
176
+ };
177
+ }
178
+ else if (((_b = gcfFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-taskqueue"]) === "true") {
167
179
  trigger = {
168
- eventType: gcfFunction.eventTrigger.eventType,
169
- eventFilters: {},
170
- retry: false,
180
+ taskQueueTrigger: {},
181
+ };
182
+ }
183
+ else if (gcfFunction.eventTrigger) {
184
+ trigger = {
185
+ eventTrigger: {
186
+ eventType: gcfFunction.eventTrigger.eventType,
187
+ eventFilters: {},
188
+ retry: false,
189
+ },
171
190
  };
172
191
  if (gcfFunction.eventTrigger.pubsubTopic) {
173
- trigger.eventFilters.resource = gcfFunction.eventTrigger.pubsubTopic;
192
+ trigger.eventTrigger.eventFilters.resource = gcfFunction.eventTrigger.pubsubTopic;
174
193
  }
175
194
  else {
176
195
  for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
177
- trigger.eventFilters[attribute] = value;
196
+ trigger.eventTrigger.eventFilters[attribute] = value;
178
197
  }
179
198
  }
199
+ proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
180
200
  }
181
201
  else {
182
- trigger = {};
202
+ trigger = { httpsTrigger: {} };
183
203
  }
184
204
  if (!runtimes.isValidRuntime(gcfFunction.buildConfig.runtime)) {
185
205
  logger_1.logger.debug("GCFv2 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
186
206
  }
187
- const cloudFunction = {
188
- platform: "gcfv2",
189
- id,
207
+ const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
190
208
  project,
191
- region,
192
- trigger,
193
- entryPoint: gcfFunction.buildConfig.entryPoint,
194
- runtime: gcfFunction.buildConfig.runtime,
195
- uri: gcfFunction.serviceConfig.uri,
196
- };
197
- proto.copyIfPresent(cloudFunction, gcfFunction.serviceConfig, "serviceAccountEmail", "availableMemoryMb", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
198
- proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
199
- proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
200
- proto.renameIfPresent(cloudFunction, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
201
- proto.copyIfPresent(cloudFunction, gcfFunction, "labels");
202
- return cloudFunction;
209
+ region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, uri: gcfFunction.serviceConfig.uri });
210
+ proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "availableMemoryMb", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
211
+ proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
212
+ proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
213
+ proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
214
+ proto.copyIfPresent(endpoint, gcfFunction, "labels");
215
+ return endpoint;
203
216
  }
204
- exports.specFromFunction = specFromFunction;
217
+ exports.endpointFromFunction = endpointFromFunction;
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.jobFromSpec = exports.createOrReplaceJob = exports.updateJob = exports.getJob = exports.deleteJob = exports.createJob = exports.assertValidJob = void 0;
3
+ exports.jobFromEndpoint = exports.createOrReplaceJob = exports.updateJob = exports.getJob = exports.deleteJob = exports.createJob = exports.assertValidJob = void 0;
4
4
  const _ = require("lodash");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
7
7
  const api = require("../api");
8
8
  const backend = require("../deploy/functions/backend");
9
9
  const proto = require("./proto");
10
+ const functional_1 = require("../functional");
10
11
  const VERSION = "v1beta1";
11
12
  const DEFAULT_TIME_ZONE = "America/Los_Angeles";
12
13
  function assertValidJob(job) {
@@ -86,21 +87,26 @@ function isIdentical(job, otherJob) {
86
87
  job.timeZone === otherJob.timeZone &&
87
88
  _.isEqual(job.retryConfig, otherJob.retryConfig));
88
89
  }
89
- function jobFromSpec(schedule, appEngineLocation) {
90
- const job = {
91
- name: backend.scheduleName(schedule, appEngineLocation),
92
- schedule: schedule.schedule,
93
- };
94
- proto.copyIfPresent(job, schedule, "timeZone", "retryConfig");
95
- if (schedule.transport === "https") {
96
- throw new error_1.FirebaseError("HTTPS transport for scheduled functions is not yet supported");
90
+ function jobFromEndpoint(endpoint, appEngineLocation) {
91
+ const job = {};
92
+ if (endpoint.platform === "gcfv1") {
93
+ const id = backend.scheduleIdForFunction(endpoint);
94
+ const region = appEngineLocation;
95
+ job.name = `projects/${endpoint.project}/locations/${region}/jobs/${id}`;
96
+ job.pubsubTarget = {
97
+ topicName: `projects/${endpoint.project}/topics/${id}`,
98
+ attributes: {
99
+ scheduled: "true",
100
+ },
101
+ };
97
102
  }
98
- job.pubsubTarget = {
99
- topicName: backend.topicName(schedule),
100
- attributes: {
101
- scheduled: "true",
102
- },
103
- };
103
+ else if (endpoint.platform === "gcfv2") {
104
+ throw new error_1.FirebaseError("Do not know how to create a scheduled GCFv2 function");
105
+ }
106
+ else {
107
+ functional_1.assertExhaustive(endpoint.platform);
108
+ }
109
+ proto.copyIfPresent(job, endpoint.scheduleTrigger, "schedule", "retryConfig", "timeZone");
104
110
  return job;
105
111
  }
106
- exports.jobFromSpec = jobFromSpec;
112
+ exports.jobFromEndpoint = jobFromEndpoint;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queueFromEndpoint = exports.queueNameForEndpoint = exports.setEnqueuer = exports.getIamPolicy = exports.setIamPolicy = exports.deleteQueue = exports.purgeQueue = exports.upsertQueue = exports.updateQueue = exports.getQueue = exports.createQueue = exports.DEFAULT_SETTINGS = void 0;
4
+ const proto = require("./proto");
5
+ const apiv2_1 = require("../apiv2");
6
+ const api_1 = require("../api");
7
+ const API_VERSION = "v2";
8
+ const client = new apiv2_1.Client({
9
+ urlPrefix: api_1.cloudTasksOrigin,
10
+ auth: true,
11
+ apiVersion: API_VERSION,
12
+ });
13
+ exports.DEFAULT_SETTINGS = {
14
+ rateLimits: {
15
+ maxConcurrentDispatches: 1000,
16
+ maxBurstSize: 100,
17
+ maxDispatchesPerSecond: 500,
18
+ },
19
+ state: "RUNNING",
20
+ retryConfig: {
21
+ maxDoublings: 16,
22
+ maxAttempts: 3,
23
+ maxBackoff: "3600s",
24
+ minBackoff: "0.100s",
25
+ },
26
+ };
27
+ async function createQueue(queue) {
28
+ const path = queue.name.substring(0, queue.name.lastIndexOf("/"));
29
+ const res = await client.post(path, queue);
30
+ return res.body;
31
+ }
32
+ exports.createQueue = createQueue;
33
+ async function getQueue(name) {
34
+ const res = await client.get(name);
35
+ return res.body;
36
+ }
37
+ exports.getQueue = getQueue;
38
+ async function updateQueue(queue) {
39
+ const res = await client.patch(queue.name, queue, {
40
+ queryParams: { updateMask: proto.fieldMasks(queue).join(",") },
41
+ });
42
+ return res.body;
43
+ }
44
+ exports.updateQueue = updateQueue;
45
+ async function upsertQueue(queue) {
46
+ var _a, _b;
47
+ try {
48
+ const existing = await module.exports.getQueue(queue.name);
49
+ if (JSON.stringify(queue) === JSON.stringify(existing)) {
50
+ return false;
51
+ }
52
+ if (existing.state === "DISABLED") {
53
+ await module.exports.purgeQueue(queue.name);
54
+ }
55
+ await module.exports.updateQueue(queue);
56
+ return false;
57
+ }
58
+ catch (err) {
59
+ if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 404) {
60
+ await module.exports.createQueue(queue);
61
+ return true;
62
+ }
63
+ throw err;
64
+ }
65
+ }
66
+ exports.upsertQueue = upsertQueue;
67
+ async function purgeQueue(name) {
68
+ await client.post(`${name}:purge`);
69
+ }
70
+ exports.purgeQueue = purgeQueue;
71
+ async function deleteQueue(name) {
72
+ await client.delete(name);
73
+ }
74
+ exports.deleteQueue = deleteQueue;
75
+ async function setIamPolicy(name, policy) {
76
+ const res = await client.post(`${name}:setIamPolicy`, {
77
+ policy,
78
+ });
79
+ return res.body;
80
+ }
81
+ exports.setIamPolicy = setIamPolicy;
82
+ async function getIamPolicy(name) {
83
+ const res = await client.post(`${name}:getIamPolicy`);
84
+ return res.body;
85
+ }
86
+ exports.getIamPolicy = getIamPolicy;
87
+ const ENQUEUER_ROLE = "roles/cloudtasks.enqueuer";
88
+ async function setEnqueuer(name, invoker, assumeEmpty = false) {
89
+ var _a, _b;
90
+ let existing;
91
+ if (assumeEmpty) {
92
+ existing = {
93
+ bindings: [],
94
+ etag: "",
95
+ version: 3,
96
+ };
97
+ }
98
+ else {
99
+ existing = await module.exports.getIamPolicy(name);
100
+ }
101
+ const [, project] = name.split("/");
102
+ const invokerMembers = proto.getInvokerMembers(invoker, project);
103
+ while (true) {
104
+ const policy = {
105
+ bindings: existing.bindings.filter((binding) => binding.role != ENQUEUER_ROLE),
106
+ etag: existing.etag,
107
+ version: existing.version,
108
+ };
109
+ if (invokerMembers.length) {
110
+ policy.bindings.push({ role: ENQUEUER_ROLE, members: invokerMembers });
111
+ }
112
+ if (JSON.stringify(policy) === JSON.stringify(existing)) {
113
+ return;
114
+ }
115
+ try {
116
+ await module.exports.setIamPolicy(name, policy);
117
+ return;
118
+ }
119
+ catch (err) {
120
+ if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
121
+ existing = await module.exports.getIamPolicy(name);
122
+ continue;
123
+ }
124
+ throw err;
125
+ }
126
+ }
127
+ }
128
+ exports.setEnqueuer = setEnqueuer;
129
+ function queueNameForEndpoint(endpoint) {
130
+ return `projects/${endpoint.project}/locations/${endpoint.region}/queues/${endpoint.id}`;
131
+ }
132
+ exports.queueNameForEndpoint = queueNameForEndpoint;
133
+ function queueFromEndpoint(endpoint) {
134
+ const queue = Object.assign(Object.assign({}, JSON.parse(JSON.stringify(exports.DEFAULT_SETTINGS))), { name: queueNameForEndpoint(endpoint) });
135
+ if (endpoint.taskQueueTrigger.rateLimits) {
136
+ proto.copyIfPresent(queue.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxBurstSize", "maxConcurrentDispatches", "maxDispatchesPerSecond");
137
+ }
138
+ if (endpoint.taskQueueTrigger.retryConfig) {
139
+ proto.copyIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoff", "maxDoublings", "maxRetryDuration", "minBackoff");
140
+ }
141
+ return queue;
142
+ }
143
+ exports.queueFromEndpoint = queueFromEndpoint;
package/lib/gcp/docker.js CHANGED
@@ -4,7 +4,7 @@ exports.Client = void 0;
4
4
  const error_1 = require("../error");
5
5
  const api = require("../apiv2");
6
6
  function isErrors(response) {
7
- return Object.prototype.hasOwnProperty.call(response, "errors");
7
+ return !!response && Object.prototype.hasOwnProperty.call(response, "errors");
8
8
  }
9
9
  const API_VERSION = "v2";
10
10
  class Client {
@@ -27,6 +27,9 @@ class Client {
27
27
  async deleteTag(path, tag) {
28
28
  var _a;
29
29
  const response = await this.client.delete(`${path}/manifests/${tag}`);
30
+ if (!response.body) {
31
+ return;
32
+ }
30
33
  if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
31
34
  throw new error_1.FirebaseError(`Failed to delete tag ${tag} at path ${path}`, {
32
35
  children: response.body.errors,
@@ -36,6 +39,9 @@ class Client {
36
39
  async deleteImage(path, digest) {
37
40
  var _a;
38
41
  const response = await this.client.delete(`${path}/manifests/${digest}`);
42
+ if (!response.body) {
43
+ return;
44
+ }
39
45
  if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
40
46
  throw new error_1.FirebaseError(`Failed to delete image ${digest} at path ${path}`, {
41
47
  children: response.body.errors,
package/lib/gcp/proto.js CHANGED
@@ -67,10 +67,10 @@ function fieldMasksHelper(prefixes, cursor, doNotRecurseIn, masks) {
67
67
  }
68
68
  }
69
69
  function getInvokerMembers(invoker, projectId) {
70
- if (invoker[0] === "private") {
70
+ if (invoker.includes("private")) {
71
71
  return [];
72
72
  }
73
- if (invoker[0] === "public") {
73
+ if (invoker.includes("public")) {
74
74
  return ["allUsers"];
75
75
  }
76
76
  return invoker.map((inv) => formatServiceAccount(inv, projectId));
package/lib/gcp/pubsub.js CHANGED
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.topicFromSpec = exports.deleteTopic = exports.updateTopic = exports.getTopic = exports.createTopic = void 0;
3
+ exports.deleteTopic = exports.updateTopic = exports.getTopic = exports.createTopic = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
- const backend = require("../deploy/functions/backend");
7
6
  const proto = require("./proto");
8
7
  const API_VERSION = "v1";
9
8
  const client = new apiv2_1.Client({
@@ -33,10 +32,3 @@ async function deleteTopic(name) {
33
32
  await client.delete(name);
34
33
  }
35
34
  exports.deleteTopic = deleteTopic;
36
- function topicFromSpec(spec) {
37
- return {
38
- name: backend.topicName(spec),
39
- labels: Object.assign({}, spec.labels),
40
- };
41
- }
42
- exports.topicFromSpec = topicFromSpec;