firebase-tools 13.15.4 → 13.17.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 (35) hide show
  1. package/lib/api.js +3 -1
  2. package/lib/apphosting/index.js +2 -1
  3. package/lib/commands/dataconnect-sql-diff.js +2 -1
  4. package/lib/commands/dataconnect-sql-migrate.js +0 -1
  5. package/lib/commands/firestore-databases-restore.js +39 -1
  6. package/lib/dataconnect/client.js +2 -1
  7. package/lib/dataconnect/schemaMigration.js +134 -48
  8. package/lib/dataconnect/types.js +1 -0
  9. package/lib/deploy/functions/prepare.js +0 -8
  10. package/lib/deploy/functions/release/planner.js +2 -1
  11. package/lib/emulator/apphosting/index.js +38 -0
  12. package/lib/emulator/apphosting/serve.js +26 -0
  13. package/lib/emulator/auth/handlers.js +27 -0
  14. package/lib/emulator/auth/operations.js +41 -3
  15. package/lib/emulator/auth/state.js +2 -1
  16. package/lib/emulator/constants.js +10 -0
  17. package/lib/emulator/controller.js +24 -3
  18. package/lib/emulator/downloadableEmulators.js +12 -12
  19. package/lib/emulator/env.js +3 -0
  20. package/lib/emulator/functionsEmulator.js +19 -0
  21. package/lib/emulator/functionsEmulatorShared.js +17 -0
  22. package/lib/emulator/portUtils.js +2 -0
  23. package/lib/emulator/registry.js +2 -0
  24. package/lib/emulator/taskQueue.js +341 -0
  25. package/lib/emulator/tasksEmulator.js +238 -0
  26. package/lib/emulator/types.js +6 -0
  27. package/lib/experiments.js +4 -0
  28. package/lib/firestore/api.js +2 -1
  29. package/lib/firestore/options.js +7 -0
  30. package/lib/gcp/cloudsql/connect.js +49 -37
  31. package/lib/gcp/cloudsql/permissions.js +160 -0
  32. package/lib/init/features/dataconnect/index.js +5 -2
  33. package/lib/utils.js +9 -1
  34. package/package.json +1 -1
  35. package/schema/firebase-config.json +24 -0
@@ -0,0 +1,341 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskQueue = exports.TaskStatus = exports.Queue = void 0;
4
+ const abort_controller_1 = require("abort-controller");
5
+ const emulatorLogger_1 = require("./emulatorLogger");
6
+ const types_1 = require("./types");
7
+ const node_fetch_1 = require("node-fetch");
8
+ class Node {
9
+ constructor(data) {
10
+ this.data = data;
11
+ this.next = null;
12
+ this.prev = null;
13
+ }
14
+ }
15
+ class Queue {
16
+ constructor(capacity = 10000) {
17
+ this.nodeMap = {};
18
+ this.count = 0;
19
+ this.first = null;
20
+ this.last = null;
21
+ this.capacity = capacity;
22
+ }
23
+ enqueue(id, item) {
24
+ if (this.count >= this.capacity) {
25
+ throw new Error("Queue has reached capacity");
26
+ }
27
+ const newNode = new Node(item);
28
+ if (this.nodeMap[id] !== undefined) {
29
+ throw new Error("Queue IDs must be unique");
30
+ }
31
+ this.nodeMap[id] = newNode;
32
+ if (!this.first) {
33
+ this.first = newNode;
34
+ }
35
+ if (this.last) {
36
+ this.last.next = newNode;
37
+ }
38
+ newNode.prev = this.last;
39
+ this.last = newNode;
40
+ this.count++;
41
+ }
42
+ peek() {
43
+ if (this.first) {
44
+ return this.first.data;
45
+ }
46
+ else {
47
+ throw new Error("Trying to peek into an empty queue");
48
+ }
49
+ }
50
+ dequeue() {
51
+ if (this.first) {
52
+ const currentFirst = this.first;
53
+ this.first = this.first.next;
54
+ if (this.last === currentFirst) {
55
+ this.last = null;
56
+ }
57
+ this.count--;
58
+ return currentFirst.data;
59
+ }
60
+ else {
61
+ throw new Error("Trying to dequeue from an empty queue");
62
+ }
63
+ }
64
+ remove(id) {
65
+ if (this.nodeMap[id] === undefined) {
66
+ throw new Error("Trying to remove a task that doesn't exist");
67
+ }
68
+ const toRemove = this.nodeMap[id];
69
+ if (toRemove.next === null && toRemove.prev === null) {
70
+ this.first = null;
71
+ this.last = null;
72
+ }
73
+ else if (toRemove.next === null) {
74
+ this.last = toRemove.prev;
75
+ toRemove.prev.next = null;
76
+ }
77
+ else if (toRemove.prev === null) {
78
+ this.first = toRemove.next;
79
+ toRemove.next.prev = null;
80
+ }
81
+ else {
82
+ const prev = toRemove.prev;
83
+ const next = toRemove.next;
84
+ prev.next = next;
85
+ next.prev = prev;
86
+ }
87
+ delete this.nodeMap[id];
88
+ this.count--;
89
+ }
90
+ getAll() {
91
+ const all = [];
92
+ let curr = this.first;
93
+ while (curr) {
94
+ all.push(curr.data);
95
+ curr = curr.next;
96
+ }
97
+ return all;
98
+ }
99
+ isEmpty() {
100
+ return this.first === null;
101
+ }
102
+ size() {
103
+ return this.count;
104
+ }
105
+ }
106
+ exports.Queue = Queue;
107
+ var TaskStatus;
108
+ (function (TaskStatus) {
109
+ TaskStatus[TaskStatus["NOT_STARTED"] = 0] = "NOT_STARTED";
110
+ TaskStatus[TaskStatus["RUNNING"] = 1] = "RUNNING";
111
+ TaskStatus[TaskStatus["RETRY"] = 2] = "RETRY";
112
+ TaskStatus[TaskStatus["FAILED"] = 3] = "FAILED";
113
+ TaskStatus[TaskStatus["FINISHED"] = 4] = "FINISHED";
114
+ })(TaskStatus = exports.TaskStatus || (exports.TaskStatus = {}));
115
+ class TaskQueue {
116
+ constructor(key, config) {
117
+ this.key = key;
118
+ this.config = config;
119
+ this.queue = new Queue();
120
+ this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.TASKS);
121
+ this.tokens = 0;
122
+ this.addedTimes = [];
123
+ this.completedTimes = [];
124
+ this.failedTimes = [];
125
+ this.maxTokens = Math.max(this.config.rateLimits.maxDispatchesPerSecond, 1.1);
126
+ this.lastTokenUpdate = Date.now();
127
+ this.queuedIds = new Set();
128
+ this.dispatches = new Array(this.config.rateLimits.maxConcurrentDispatches).fill(null);
129
+ this.openDispatches = Array.from(this.dispatches.keys());
130
+ }
131
+ dispatchTasks() {
132
+ while (!this.queue.isEmpty() && this.openDispatches.length > 0 && this.tokens >= 1) {
133
+ const dispatchLocation = this.openDispatches.pop();
134
+ if (dispatchLocation !== undefined) {
135
+ const dispatch = this.queue.dequeue();
136
+ dispatch.metadata.lastRunTime = null;
137
+ dispatch.metadata.currentAttempt = 1;
138
+ dispatch.metadata.status = TaskStatus.NOT_STARTED;
139
+ dispatch.metadata.startTime = Date.now();
140
+ this.dispatches[dispatchLocation] = dispatch;
141
+ this.tokens--;
142
+ }
143
+ }
144
+ }
145
+ setDispatch(dispatches) {
146
+ this.dispatches = dispatches;
147
+ const open = [];
148
+ for (let i = 0; i < this.dispatches.length; i++) {
149
+ if (dispatches[i] === null) {
150
+ open.push(i);
151
+ }
152
+ }
153
+ this.openDispatches = open;
154
+ }
155
+ getDispatch() {
156
+ return this.dispatches;
157
+ }
158
+ processDispatch() {
159
+ var _a;
160
+ for (let i = 0; i < this.dispatches.length; i++) {
161
+ if (this.dispatches[i] !== null) {
162
+ switch ((_a = this.dispatches[i]) === null || _a === void 0 ? void 0 : _a.metadata.status) {
163
+ case TaskStatus.FAILED:
164
+ this.dispatches[i] = null;
165
+ this.openDispatches.push(i);
166
+ this.completedTimes.push(Date.now());
167
+ this.failedTimes.push(Date.now());
168
+ break;
169
+ case TaskStatus.NOT_STARTED:
170
+ void this.runTask(i);
171
+ break;
172
+ case TaskStatus.RETRY:
173
+ this.handleRetry(i);
174
+ break;
175
+ case TaskStatus.FINISHED:
176
+ this.dispatches[i] = null;
177
+ this.openDispatches.push(i);
178
+ this.completedTimes.push(Date.now());
179
+ break;
180
+ }
181
+ }
182
+ }
183
+ }
184
+ async runTask(dispatchIndex) {
185
+ if (this.dispatches[dispatchIndex] === null) {
186
+ throw new Error("Trying to dispatch a nonexistent task");
187
+ }
188
+ const emulatedTask = this.dispatches[dispatchIndex];
189
+ if (emulatedTask.metadata.lastRunTime !== null &&
190
+ Date.now() - emulatedTask.metadata.lastRunTime < emulatedTask.metadata.currentBackoff * 1000) {
191
+ return;
192
+ }
193
+ emulatedTask.metadata.status = TaskStatus.RUNNING;
194
+ try {
195
+ const headers = Object.assign({ "Content-Type": "application/json", "X-CloudTasks-QueueName": this.key, "X-CloudTasks-TaskName": emulatedTask.task.name.split("/").pop(), "X-CloudTasks-TaskRetryCount": `${emulatedTask.metadata.currentAttempt - 1}`, "X-CloudTasks-TaskExecutionCount": `${emulatedTask.metadata.executionCount}`, "X-CloudTasks-TaskETA": `${emulatedTask.task.scheduleTime || Date.now()}` }, emulatedTask.task.httpRequest.headers);
196
+ if (emulatedTask.metadata.previousResponse) {
197
+ headers["X-CloudTasks-TaskPreviousResponse"] = `${emulatedTask.metadata.previousResponse}`;
198
+ }
199
+ const controller = new abort_controller_1.default();
200
+ const signal = controller.signal;
201
+ const request = (0, node_fetch_1.default)(emulatedTask.task.httpRequest.url, {
202
+ method: "POST",
203
+ headers: headers,
204
+ body: JSON.stringify(emulatedTask.task.httpRequest.body),
205
+ signal: signal,
206
+ });
207
+ const dispatchDeadline = emulatedTask.task.dispatchDeadline;
208
+ const dispatchDeadlineSeconds = dispatchDeadline
209
+ ? parseInt(dispatchDeadline.substring(0, dispatchDeadline.length - 1))
210
+ : 60;
211
+ const abortId = setTimeout(() => {
212
+ controller.abort();
213
+ }, dispatchDeadlineSeconds * 1000);
214
+ const response = await request;
215
+ clearTimeout(abortId);
216
+ if (response.ok) {
217
+ emulatedTask.metadata.status = TaskStatus.FINISHED;
218
+ return;
219
+ }
220
+ else {
221
+ if (!(response.status >= 500 && response.status <= 599)) {
222
+ emulatedTask.metadata.executionCount++;
223
+ }
224
+ emulatedTask.metadata.previousResponse = response.status;
225
+ emulatedTask.metadata.status = TaskStatus.RETRY;
226
+ emulatedTask.metadata.lastRunTime = Date.now();
227
+ }
228
+ }
229
+ catch (e) {
230
+ this.logger.logLabeled("WARN", `${e}`);
231
+ emulatedTask.metadata.status = TaskStatus.RETRY;
232
+ emulatedTask.metadata.lastRunTime = Date.now();
233
+ }
234
+ }
235
+ handleRetry(dispatchIndex) {
236
+ if (this.dispatches[dispatchIndex] === null) {
237
+ throw new Error("Trying to retry a nonexistent task");
238
+ }
239
+ const { metadata } = this.dispatches[dispatchIndex];
240
+ const { retryConfig } = this.config;
241
+ if (this.shouldStopRetrying(metadata, retryConfig)) {
242
+ metadata.status = TaskStatus.FAILED;
243
+ return;
244
+ }
245
+ this.updateMetadata(metadata, retryConfig);
246
+ metadata.status = TaskStatus.NOT_STARTED;
247
+ }
248
+ shouldStopRetrying(metadata, retryOptions) {
249
+ if (metadata.currentAttempt > retryOptions.maxAttempts) {
250
+ if (retryOptions.maxRetrySeconds === null || retryOptions.maxRetrySeconds === 0) {
251
+ return true;
252
+ }
253
+ if (Date.now() - metadata.startTime > retryOptions.maxRetrySeconds * 1000) {
254
+ return true;
255
+ }
256
+ }
257
+ return false;
258
+ }
259
+ updateMetadata(metadata, retryOptions) {
260
+ const timeMultplier = Math.pow(2, Math.min(metadata.currentAttempt - 1, retryOptions.maxDoublings)) +
261
+ Math.max(0, metadata.currentAttempt - retryOptions.maxDoublings - 1) *
262
+ Math.pow(2, retryOptions.maxDoublings);
263
+ metadata.currentBackoff = Math.min(retryOptions.maxBackoffSeconds, timeMultplier * retryOptions.minBackoffSeconds);
264
+ metadata.currentAttempt++;
265
+ }
266
+ isActive() {
267
+ return !this.queue.isEmpty() || this.dispatches.some((e) => e !== null);
268
+ }
269
+ refillTokens() {
270
+ const tokensToAdd = ((Date.now() - this.lastTokenUpdate) / 1000) * this.config.rateLimits.maxDispatchesPerSecond;
271
+ this.addTokens(tokensToAdd);
272
+ this.lastTokenUpdate = Date.now();
273
+ }
274
+ addTokens(t) {
275
+ this.tokens += t;
276
+ this.tokens = Math.min(this.tokens, this.maxTokens);
277
+ }
278
+ setTokens(t) {
279
+ this.tokens = t;
280
+ }
281
+ getTokens() {
282
+ return this.tokens;
283
+ }
284
+ enqueue(task) {
285
+ if (this.queuedIds.has(task.name)) {
286
+ throw new Error(`A task has already been queued with id ${task.name}`);
287
+ }
288
+ const emulatedTask = {
289
+ task: task,
290
+ metadata: {
291
+ currentAttempt: 0,
292
+ currentBackoff: 0,
293
+ startTime: 0,
294
+ status: TaskStatus.NOT_STARTED,
295
+ lastRunTime: null,
296
+ previousResponse: null,
297
+ executionCount: 0,
298
+ },
299
+ };
300
+ emulatedTask.task.httpRequest.url =
301
+ emulatedTask.task.httpRequest.url === ""
302
+ ? this.config.defaultUri
303
+ : emulatedTask.task.httpRequest.url;
304
+ this.queue.enqueue(emulatedTask.task.name, emulatedTask);
305
+ this.queuedIds.add(task.name);
306
+ this.addedTimes.push(Date.now());
307
+ }
308
+ delete(taskId) {
309
+ this.queue.remove(taskId);
310
+ }
311
+ getDebugInfo() {
312
+ return `
313
+ Task Queue (${this.key}):
314
+ - Active: ${this.isActive().toString()}
315
+ - Tokens: ${this.tokens}
316
+ - In Queue: ${this.queue.size()}
317
+ - Dispatch: [
318
+ ${this.dispatches.map((t) => (t === null ? "empty" : t.task.name)).join(",\n")}
319
+ ]
320
+ - Open Locations: [${this.openDispatches.join(", ")}]
321
+ `;
322
+ }
323
+ getStatistics() {
324
+ const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
325
+ const oneMinuteAgo = Date.now() - 60 * 1000;
326
+ this.addedTimes = this.addedTimes.filter((t) => t > fiveMinutesAgo);
327
+ this.failedTimes = this.failedTimes.filter((t) => t > fiveMinutesAgo);
328
+ this.completedTimes = this.completedTimes.filter((t) => t > oneMinuteAgo);
329
+ return {
330
+ numberOfTasks: this.queue.size(),
331
+ tasksAdded: this.addedTimes.length / 5,
332
+ completedLastMin: this.completedTimes.length,
333
+ failedTasks: this.failedTimes.length / 5,
334
+ runningTasks: this.dispatches.length,
335
+ maxRate: this.config.rateLimits.maxDispatchesPerSecond,
336
+ maxConcurrent: this.config.rateLimits.maxConcurrentDispatches,
337
+ };
338
+ }
339
+ }
340
+ exports.TaskQueue = TaskQueue;
341
+ TaskQueue.TASK_QUEUE_INTERVAL = 1000;
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TasksEmulator = exports.TaskQueueController = void 0;
4
+ const express = require("express");
5
+ const constants_1 = require("./constants");
6
+ const types_1 = require("./types");
7
+ const utils_1 = require("../utils");
8
+ const emulatorLogger_1 = require("./emulatorLogger");
9
+ const taskQueue_1 = require("./taskQueue");
10
+ const cors = require("cors");
11
+ const RETRY_CONFIG_DEFAULTS = {
12
+ maxAttempts: 3,
13
+ maxRetrySeconds: null,
14
+ maxBackoffSeconds: 60 * 60,
15
+ maxDoublings: 16,
16
+ minBackoffSeconds: 0.1,
17
+ };
18
+ const RATE_LIMITS_DEFAULT = {
19
+ maxConcurrentDispatches: 1000,
20
+ maxDispatchesPerSecond: 500,
21
+ };
22
+ class TaskQueueController {
23
+ constructor() {
24
+ this.queues = {};
25
+ this.tokenRefillIds = [];
26
+ this.running = false;
27
+ this.listenId = null;
28
+ }
29
+ enqueue(key, task) {
30
+ if (!this.queues[key]) {
31
+ throw new Error("Queue does not exist");
32
+ }
33
+ this.queues[key].enqueue(task);
34
+ }
35
+ delete(key, taskId) {
36
+ if (!this.queues[key]) {
37
+ throw new Error("Queue does not exist");
38
+ }
39
+ this.queues[key].delete(taskId);
40
+ }
41
+ createQueue(key, config) {
42
+ const newQueue = new taskQueue_1.TaskQueue(key, config);
43
+ const intervalID = setInterval(() => newQueue.refillTokens(), TaskQueueController.TOKEN_REFRESH_INTERVAL);
44
+ this.tokenRefillIds.push(intervalID);
45
+ this.queues[key] = newQueue;
46
+ }
47
+ listen() {
48
+ let shouldUpdate = false;
49
+ for (const [_key, queue] of Object.entries(this.queues)) {
50
+ shouldUpdate = shouldUpdate || queue.isActive();
51
+ }
52
+ if (shouldUpdate) {
53
+ this.updateQueues();
54
+ this.listenId = setTimeout(() => this.listen(), TaskQueueController.UPDATE_TIMEOUT);
55
+ }
56
+ else {
57
+ this.listenId = setTimeout(() => this.listen(), TaskQueueController.LISTEN_TIMEOUT);
58
+ }
59
+ }
60
+ updateQueues() {
61
+ for (const [_key, queue] of Object.entries(this.queues)) {
62
+ if (queue.isActive()) {
63
+ queue.dispatchTasks();
64
+ queue.processDispatch();
65
+ }
66
+ }
67
+ }
68
+ start() {
69
+ this.running = true;
70
+ this.listen();
71
+ }
72
+ stop() {
73
+ if (this.listenId) {
74
+ clearTimeout(this.listenId);
75
+ }
76
+ this.tokenRefillIds.forEach(clearInterval);
77
+ this.running = false;
78
+ }
79
+ isRunning() {
80
+ return this.running;
81
+ }
82
+ getStatistics() {
83
+ const stats = {};
84
+ for (const [key, queue] of Object.entries(this.queues)) {
85
+ stats[key] = queue.getStatistics();
86
+ }
87
+ return stats;
88
+ }
89
+ }
90
+ exports.TaskQueueController = TaskQueueController;
91
+ TaskQueueController.UPDATE_TIMEOUT = 0;
92
+ TaskQueueController.LISTEN_TIMEOUT = 1000;
93
+ TaskQueueController.TOKEN_REFRESH_INTERVAL = 1000;
94
+ class TasksEmulator {
95
+ constructor(args) {
96
+ this.args = args;
97
+ this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.TASKS);
98
+ this.controller = new TaskQueueController();
99
+ }
100
+ validateQueueId(queueId) {
101
+ if (typeof queueId !== "string") {
102
+ return false;
103
+ }
104
+ if (queueId.length > 100) {
105
+ return false;
106
+ }
107
+ const regex = /^[A-Za-z0-9-]+$/;
108
+ return regex.test(queueId);
109
+ }
110
+ createHubServer() {
111
+ const hub = express();
112
+ const createTaskQueueRoute = `/projects/:project_id/locations/:location_id/queues/:queue_name`;
113
+ const createTaskQueueHandler = (req, res) => {
114
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
115
+ const projectId = req.params.project_id;
116
+ const locationId = req.params.location_id;
117
+ const queueName = req.params.queue_name;
118
+ if (!this.validateQueueId(queueName)) {
119
+ res.status(400).send("Invalid Queue ID");
120
+ return;
121
+ }
122
+ const key = `queue:${projectId}-${locationId}-${queueName}`;
123
+ this.logger.logLabeled("SUCCESS", "tasks", `Created queue with key: ${key}`);
124
+ const body = req.body;
125
+ const taskQueueConfig = {
126
+ retryConfig: {
127
+ maxAttempts: (_b = (_a = body.retryConfig) === null || _a === void 0 ? void 0 : _a.maxAttempts) !== null && _b !== void 0 ? _b : RETRY_CONFIG_DEFAULTS.maxAttempts,
128
+ maxRetrySeconds: (_d = (_c = body.retryConfig) === null || _c === void 0 ? void 0 : _c.maxRetrySeconds) !== null && _d !== void 0 ? _d : RETRY_CONFIG_DEFAULTS.maxRetrySeconds,
129
+ maxBackoffSeconds: (_f = (_e = body.retryConfig) === null || _e === void 0 ? void 0 : _e.maxBackoffSeconds) !== null && _f !== void 0 ? _f : RETRY_CONFIG_DEFAULTS.maxBackoffSeconds,
130
+ maxDoublings: (_h = (_g = body.retryConfig) === null || _g === void 0 ? void 0 : _g.maxDoublings) !== null && _h !== void 0 ? _h : RETRY_CONFIG_DEFAULTS.maxDoublings,
131
+ minBackoffSeconds: (_k = (_j = body.retryConfig) === null || _j === void 0 ? void 0 : _j.minBackoffSeconds) !== null && _k !== void 0 ? _k : RETRY_CONFIG_DEFAULTS.minBackoffSeconds,
132
+ },
133
+ rateLimits: {
134
+ maxConcurrentDispatches: (_m = (_l = body.rateLimits) === null || _l === void 0 ? void 0 : _l.maxConcurrentDispatches) !== null && _m !== void 0 ? _m : RATE_LIMITS_DEFAULT.maxConcurrentDispatches,
135
+ maxDispatchesPerSecond: (_p = (_o = body.rateLimits) === null || _o === void 0 ? void 0 : _o.maxDispatchesPerSecond) !== null && _p !== void 0 ? _p : RATE_LIMITS_DEFAULT.maxDispatchesPerSecond,
136
+ },
137
+ timeoutSeconds: (_q = body.timeoutSeconds) !== null && _q !== void 0 ? _q : 10,
138
+ retry: (_r = body.retry) !== null && _r !== void 0 ? _r : false,
139
+ defaultUri: body.defaultUri,
140
+ };
141
+ if (taskQueueConfig.rateLimits.maxConcurrentDispatches > 5000) {
142
+ res.status(400).send("cannot set maxConcurrentDispatches to a value over 5000");
143
+ return;
144
+ }
145
+ this.controller.createQueue(key, taskQueueConfig);
146
+ this.logger.log("DEBUG", `Created task queue ${key} with configuration: ${JSON.stringify(taskQueueConfig)}`);
147
+ res.status(200).send({ taskQueueConfig });
148
+ };
149
+ const enqueueTasksRoute = `/projects/:project_id/locations/:location_id/queues/:queue_name/tasks`;
150
+ const enqueueTasksHandler = (req, res) => {
151
+ var _a;
152
+ if (!this.controller.isRunning()) {
153
+ this.controller.start();
154
+ }
155
+ const projectId = req.params.project_id;
156
+ const locationId = req.params.location_id;
157
+ const queueName = req.params.queue_name;
158
+ const queueKey = `queue:${projectId}-${locationId}-${queueName}`;
159
+ if (!this.controller.queues[queueKey]) {
160
+ this.logger.log("WARN", "Tried to queue a task into a non-existent queue");
161
+ res.status(404).send("Tried to queue a task from a non-existent queue");
162
+ return;
163
+ }
164
+ req.body.task.name =
165
+ (_a = req.body.task.name) !== null && _a !== void 0 ? _a : `/projects/${projectId}/locations/${locationId}/queues/${queueName}/tasks/${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
166
+ req.body.task.httpRequest.body = JSON.parse(atob(req.body.task.httpRequest.body));
167
+ const task = req.body.task;
168
+ try {
169
+ this.controller.enqueue(queueKey, task);
170
+ this.logger.log("DEBUG", `Enqueueing task ${task.name} onto ${queueKey}`);
171
+ res.status(200).send({ task: task });
172
+ }
173
+ catch (e) {
174
+ res.status(409).send("A task with the same name already exists");
175
+ }
176
+ };
177
+ const deleteTasksRoute = `/projects/:project_id/locations/:location_id/queues/:queue_name/tasks/:task_id`;
178
+ const deleteTasksHandler = (req, res) => {
179
+ const projectId = req.params.project_id;
180
+ const locationId = req.params.location_id;
181
+ const queueName = req.params.queue_name;
182
+ const taskId = req.params.task_id;
183
+ const queueKey = `queue:${projectId}-${locationId}-${queueName}`;
184
+ if (!this.controller.queues[queueKey]) {
185
+ this.logger.log("WARN", "Tried to remove a task from a non-existent queue");
186
+ res.status(404).send("Tried to remove a task from a non-existent queue");
187
+ return;
188
+ }
189
+ try {
190
+ const taskName = `projects/${projectId}/locations/${locationId}/queues/${queueName}/tasks/${taskId}`;
191
+ this.logger.log("DEBUG", `removing: ${taskName}`);
192
+ this.controller.delete(queueKey, taskName);
193
+ res.status(200).send({ res: "OK" });
194
+ }
195
+ catch (e) {
196
+ this.logger.log("WARN", "Tried to remove a task that doesn't exist");
197
+ res.status(404).send("Tried to remove a task that doesn't exist");
198
+ }
199
+ };
200
+ const getStatsRoute = `/queueStats`;
201
+ const getStatsHandler = (req, res) => {
202
+ res.json(this.controller.getStatistics());
203
+ };
204
+ hub.get([getStatsRoute], cors({ origin: true }), getStatsHandler);
205
+ hub.post([createTaskQueueRoute], express.json(), createTaskQueueHandler);
206
+ hub.post([enqueueTasksRoute], express.json(), enqueueTasksHandler);
207
+ hub.delete([deleteTasksRoute], express.json(), deleteTasksHandler);
208
+ return hub;
209
+ }
210
+ async start() {
211
+ const { host, port } = this.getInfo();
212
+ const server = this.createHubServer().listen(port, host);
213
+ this.destroyServer = (0, utils_1.createDestroyer)(server);
214
+ return Promise.resolve();
215
+ }
216
+ async connect() {
217
+ return Promise.resolve();
218
+ }
219
+ async stop() {
220
+ if (this.destroyServer) {
221
+ await this.destroyServer();
222
+ }
223
+ this.controller.stop();
224
+ }
225
+ getInfo() {
226
+ const host = this.args.host || constants_1.Constants.getDefaultHost();
227
+ const port = this.args.port || constants_1.Constants.getDefaultPort(types_1.Emulators.TASKS);
228
+ return {
229
+ name: this.getName(),
230
+ host,
231
+ port,
232
+ };
233
+ }
234
+ getName() {
235
+ return types_1.Emulators.TASKS;
236
+ }
237
+ }
238
+ exports.TasksEmulator = TasksEmulator;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Severity = exports.EmulatorLog = exports.FunctionsExecutionMode = exports.isEmulator = exports.isDownloadableEmulator = exports.ALL_EMULATORS = exports.EMULATORS_SUPPORTED_BY_USE_EMULATOR = exports.EMULATORS_SUPPORTED_BY_UI = exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = exports.ALL_SERVICE_EMULATORS = exports.IMPORT_EXPORT_EMULATORS = exports.DOWNLOADABLE_EMULATORS = exports.Emulators = void 0;
4
+ const experiments = require("../experiments");
4
5
  var Emulators;
5
6
  (function (Emulators) {
6
7
  Emulators["AUTH"] = "auth";
@@ -9,6 +10,7 @@ var Emulators;
9
10
  Emulators["FIRESTORE"] = "firestore";
10
11
  Emulators["DATABASE"] = "database";
11
12
  Emulators["HOSTING"] = "hosting";
13
+ Emulators["APPHOSTING"] = "apphosting";
12
14
  Emulators["PUBSUB"] = "pubsub";
13
15
  Emulators["UI"] = "ui";
14
16
  Emulators["LOGGING"] = "logging";
@@ -16,6 +18,7 @@ var Emulators;
16
18
  Emulators["EXTENSIONS"] = "extensions";
17
19
  Emulators["EVENTARC"] = "eventarc";
18
20
  Emulators["DATACONNECT"] = "dataconnect";
21
+ Emulators["TASKS"] = "tasks";
19
22
  })(Emulators = exports.Emulators || (exports.Emulators = {}));
20
23
  exports.DOWNLOADABLE_EMULATORS = [
21
24
  Emulators.FIRESTORE,
@@ -37,10 +40,12 @@ exports.ALL_SERVICE_EMULATORS = [
37
40
  Emulators.FIRESTORE,
38
41
  Emulators.DATABASE,
39
42
  Emulators.HOSTING,
43
+ ...(experiments.isEnabled("emulatorapphosting") ? [Emulators.APPHOSTING] : []),
40
44
  Emulators.PUBSUB,
41
45
  Emulators.STORAGE,
42
46
  Emulators.EVENTARC,
43
47
  Emulators.DATACONNECT,
48
+ Emulators.TASKS,
44
49
  ].filter((v) => v);
45
50
  exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = [
46
51
  Emulators.FIRESTORE,
@@ -48,6 +53,7 @@ exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = [
48
53
  Emulators.PUBSUB,
49
54
  Emulators.STORAGE,
50
55
  Emulators.EVENTARC,
56
+ Emulators.TASKS,
51
57
  ];
52
58
  exports.EMULATORS_SUPPORTED_BY_UI = [
53
59
  Emulators.AUTH,
@@ -56,6 +56,10 @@ exports.ALL_EXPERIMENTS = experiments({
56
56
  emulatoruisnapshot: {
57
57
  shortDescription: "Load pre-release versions of the emulator UI",
58
58
  },
59
+ emulatorapphosting: {
60
+ shortDescription: "App Hosting emulator",
61
+ public: false,
62
+ },
59
63
  webframeworks: {
60
64
  shortDescription: "Native support for popular web frameworks",
61
65
  fullDescription: "Adds support for popular web frameworks such as Next.js " +
@@ -459,11 +459,12 @@ class FirestoreApi {
459
459
  }
460
460
  return database;
461
461
  }
462
- async restoreDatabase(project, databaseId, backupName) {
462
+ async restoreDatabase(project, databaseId, backupName, encryptionConfig) {
463
463
  const url = `/projects/${project}/databases:restore`;
464
464
  const payload = {
465
465
  databaseId,
466
466
  backup: backupName,
467
+ encryptionConfig: encryptionConfig,
467
468
  };
468
469
  const options = { queryParams: { databaseId: databaseId } };
469
470
  const res = await this.apiClient.post(url, payload, options);
@@ -1,2 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EncryptionType = void 0;
4
+ var EncryptionType;
5
+ (function (EncryptionType) {
6
+ EncryptionType["CUSTOMER_MANAGED_ENCRYPTION"] = "CUSTOMER_MANAGED_ENCRYPTION";
7
+ EncryptionType["USE_SOURCE_ENCRYPTION"] = "USE_SOURCE_ENCRYPTION";
8
+ EncryptionType["GOOGLE_DEFAULT_ENCRYPTION"] = "GOOGLE_DEFAULT_ENCRYPTION";
9
+ })(EncryptionType = exports.EncryptionType || (exports.EncryptionType = {}));