firebase-tools 11.7.0 → 11.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/auth.js +1 -1
- package/lib/deploy/functions/build.js +39 -25
- package/lib/deploy/functions/cache/applyHash.js +29 -0
- package/lib/deploy/functions/cache/hash.js +30 -0
- package/lib/deploy/functions/cel.js +249 -0
- package/lib/deploy/functions/functionsDeployHelper.js +12 -1
- package/lib/deploy/functions/params.js +259 -102
- package/lib/deploy/functions/prepare.js +34 -4
- package/lib/deploy/functions/prepareFunctionsUpload.js +13 -5
- package/lib/deploy/functions/release/fabricator.js +17 -1
- package/lib/deploy/functions/release/planner.js +17 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +9 -0
- package/lib/deploy/functions/services/index.js +11 -0
- package/lib/deploy/functions/services/remoteConfig.js +14 -0
- package/lib/emulator/extensionsEmulator.js +1 -0
- package/lib/emulator/functionsEmulator.js +18 -59
- package/lib/emulator/functionsRuntimeWorker.js +38 -7
- package/lib/emulator/storage/apis/firebase.js +139 -125
- package/lib/emulator/storage/apis/gcloud.js +102 -42
- package/lib/emulator/storage/files.js +25 -15
- package/lib/emulator/storage/metadata.js +86 -56
- package/lib/emulator/storage/rules/runtime.js +10 -2
- package/lib/emulator/storage/upload.js +45 -9
- package/lib/extensions/extensionsHelper.js +1 -1
- package/lib/functions/constants.js +14 -0
- package/lib/functions/events/v2.js +2 -1
- package/lib/functions/secrets.js +8 -1
- package/lib/gcp/cloudfunctions.js +15 -18
- package/lib/gcp/cloudfunctionsv2.js +15 -18
- package/lib/gcp/cloudscheduler.js +2 -1
- package/lib/gcp/secretManager.js +15 -1
- package/lib/gcp/storage.js +15 -1
- package/lib/previews.js +1 -1
- package/lib/track.js +1 -1
- package/npm-shrinkwrap.json +54 -30
- package/package.json +6 -5
- package/templates/init/storage/storage.rules +1 -1
|
@@ -15,6 +15,45 @@ const request_1 = require("../../shared/request");
|
|
|
15
15
|
function createCloudEndpoints(emulator) {
|
|
16
16
|
const gcloudStorageAPI = (0, express_1.Router)();
|
|
17
17
|
const { adminStorageLayer, uploadService } = emulator;
|
|
18
|
+
if (process.env.STORAGE_EMULATOR_DEBUG) {
|
|
19
|
+
gcloudStorageAPI.use((req, res, next) => {
|
|
20
|
+
console.log("--------------INCOMING REQUEST--------------");
|
|
21
|
+
console.log(`${req.method.toUpperCase()} ${req.path}`);
|
|
22
|
+
console.log("-- query:");
|
|
23
|
+
console.log(JSON.stringify(req.query, undefined, 2));
|
|
24
|
+
console.log("-- headers:");
|
|
25
|
+
console.log(JSON.stringify(req.headers, undefined, 2));
|
|
26
|
+
console.log("-- body:");
|
|
27
|
+
if (req.body instanceof Buffer) {
|
|
28
|
+
console.log(`Buffer of ${req.body.length}`);
|
|
29
|
+
}
|
|
30
|
+
else if (req.body) {
|
|
31
|
+
console.log(req.body);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.log("Empty body (could be stream)");
|
|
35
|
+
}
|
|
36
|
+
const resJson = res.json.bind(res);
|
|
37
|
+
res.json = (...args) => {
|
|
38
|
+
console.log("-- response:");
|
|
39
|
+
args.forEach((data) => console.log(JSON.stringify(data, undefined, 2)));
|
|
40
|
+
return resJson.call(res, ...args);
|
|
41
|
+
};
|
|
42
|
+
const resSendStatus = res.sendStatus.bind(res);
|
|
43
|
+
res.sendStatus = (status) => {
|
|
44
|
+
console.log("-- response status:");
|
|
45
|
+
console.log(status);
|
|
46
|
+
return resSendStatus.call(res, status);
|
|
47
|
+
};
|
|
48
|
+
const resStatus = res.status.bind(res);
|
|
49
|
+
res.status = (status) => {
|
|
50
|
+
console.log("-- response status:");
|
|
51
|
+
console.log(status);
|
|
52
|
+
return resStatus.call(res, status);
|
|
53
|
+
};
|
|
54
|
+
next();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
18
57
|
gcloudStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
|
|
19
58
|
adminStorageLayer.createBucket(req.params[0]);
|
|
20
59
|
next();
|
|
@@ -25,7 +64,11 @@ function createCloudEndpoints(emulator) {
|
|
|
25
64
|
items: await adminStorageLayer.listBuckets(),
|
|
26
65
|
});
|
|
27
66
|
});
|
|
28
|
-
gcloudStorageAPI.get([
|
|
67
|
+
gcloudStorageAPI.get([
|
|
68
|
+
"/b/:bucketId/o/:objectId",
|
|
69
|
+
"/download/storage/v1/b/:bucketId/o/:objectId",
|
|
70
|
+
"/storage/v1/b/:bucketId/o/:objectId",
|
|
71
|
+
], async (req, res) => {
|
|
29
72
|
let getObjectResponse;
|
|
30
73
|
try {
|
|
31
74
|
getObjectResponse = await adminStorageLayer.getObject({
|
|
@@ -87,7 +130,7 @@ function createCloudEndpoints(emulator) {
|
|
|
87
130
|
throw err;
|
|
88
131
|
}
|
|
89
132
|
return res.status(200).json({
|
|
90
|
-
kind: "#
|
|
133
|
+
kind: "storage#objects",
|
|
91
134
|
nextPageToken: listResponse.nextPageToken,
|
|
92
135
|
prefixes: listResponse.prefixes,
|
|
93
136
|
items: (_a = listResponse.items) === null || _a === void 0 ? void 0 : _a.map((item) => new metadata_1.CloudStorageObjectMetadata(item)),
|
|
@@ -177,11 +220,8 @@ function createCloudEndpoints(emulator) {
|
|
|
177
220
|
});
|
|
178
221
|
});
|
|
179
222
|
gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
return res.sendStatus(400);
|
|
183
|
-
}
|
|
184
|
-
if (req.query.uploadType === "resumable") {
|
|
223
|
+
const uploadType = req.query.uploadType || req.header("X-Goog-Upload-Protocol");
|
|
224
|
+
if (uploadType === "resumable") {
|
|
185
225
|
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
186
226
|
if (emulatorInfo === undefined) {
|
|
187
227
|
return res.sendStatus(500);
|
|
@@ -204,45 +244,65 @@ function createCloudEndpoints(emulator) {
|
|
|
204
244
|
uploadUrl.searchParams.set("upload_id", upload.id);
|
|
205
245
|
return res.header("location", uploadUrl.toString()).sendStatus(200);
|
|
206
246
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
247
|
+
async function finalizeOneShotUpload(upload) {
|
|
248
|
+
let metadata;
|
|
249
|
+
try {
|
|
250
|
+
metadata = await adminStorageLayer.uploadObject(upload);
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
254
|
+
return res.sendStatus(403);
|
|
255
|
+
}
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
return res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
211
259
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
code: 400,
|
|
217
|
-
message: err.message,
|
|
218
|
-
},
|
|
219
|
-
});
|
|
260
|
+
if (uploadType === "multipart") {
|
|
261
|
+
const contentTypeHeader = req.header("content-type") || req.header("x-upload-content-type");
|
|
262
|
+
if (!contentTypeHeader) {
|
|
263
|
+
return res.sendStatus(400);
|
|
220
264
|
}
|
|
221
|
-
|
|
265
|
+
let metadataRaw;
|
|
266
|
+
let dataRaw;
|
|
267
|
+
try {
|
|
268
|
+
({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await (0, request_1.reqBodyToBuffer)(req)));
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
if (err instanceof Error) {
|
|
272
|
+
return res.status(400).json({
|
|
273
|
+
error: {
|
|
274
|
+
code: 400,
|
|
275
|
+
message: err.message,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
const name = getIncomingFileNameFromRequest(req.query, JSON.parse(metadataRaw));
|
|
282
|
+
if (name === undefined) {
|
|
283
|
+
res.sendStatus(400);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const upload = uploadService.multipartUpload({
|
|
287
|
+
bucketId: req.params.bucketId,
|
|
288
|
+
objectId: name,
|
|
289
|
+
metadataRaw: metadataRaw,
|
|
290
|
+
dataRaw: dataRaw,
|
|
291
|
+
authorization: req.header("authorization"),
|
|
292
|
+
});
|
|
293
|
+
return await finalizeOneShotUpload(upload);
|
|
222
294
|
}
|
|
223
|
-
const name =
|
|
224
|
-
if (name
|
|
295
|
+
const name = req.query.name;
|
|
296
|
+
if (!name) {
|
|
225
297
|
res.sendStatus(400);
|
|
226
|
-
return;
|
|
227
298
|
}
|
|
228
|
-
const upload = uploadService.
|
|
299
|
+
const upload = uploadService.mediaUpload({
|
|
229
300
|
bucketId: req.params.bucketId,
|
|
230
|
-
objectId: name,
|
|
231
|
-
|
|
232
|
-
dataRaw: dataRaw,
|
|
301
|
+
objectId: name.toString(),
|
|
302
|
+
dataRaw: await (0, request_1.reqBodyToBuffer)(req),
|
|
233
303
|
authorization: req.header("authorization"),
|
|
234
304
|
});
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
metadata = await adminStorageLayer.uploadObject(upload);
|
|
238
|
-
}
|
|
239
|
-
catch (err) {
|
|
240
|
-
if (err instanceof errors_1.ForbiddenError) {
|
|
241
|
-
return res.sendStatus(403);
|
|
242
|
-
}
|
|
243
|
-
throw err;
|
|
244
|
-
}
|
|
245
|
-
return res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
305
|
+
return await finalizeOneShotUpload(upload);
|
|
246
306
|
});
|
|
247
307
|
gcloudStorageAPI.get("/:bucketId/:objectId(**)", async (req, res) => {
|
|
248
308
|
let getObjectResponse;
|
|
@@ -324,11 +384,11 @@ function sendFileBytes(md, data, req, res) {
|
|
|
324
384
|
data = (0, zlib_1.gunzipSync)(data);
|
|
325
385
|
}
|
|
326
386
|
res.setHeader("Accept-Ranges", "bytes");
|
|
327
|
-
res.setHeader("Content-Type", md.contentType);
|
|
328
|
-
res.setHeader("Content-Disposition", md.contentDisposition);
|
|
329
|
-
res.setHeader("Content-Encoding", isGZipped ? "identity" : md.contentEncoding);
|
|
387
|
+
res.setHeader("Content-Type", md.contentType || "application/octet-stream");
|
|
388
|
+
res.setHeader("Content-Disposition", md.contentDisposition || "attachment");
|
|
389
|
+
res.setHeader("Content-Encoding", isGZipped ? "identity" : md.contentEncoding || "");
|
|
330
390
|
res.setHeader("ETag", md.etag);
|
|
331
|
-
res.setHeader("Cache-Control", md.cacheControl);
|
|
391
|
+
res.setHeader("Cache-Control", md.cacheControl || "");
|
|
332
392
|
res.setHeader("x-goog-generation", `${md.generation}`);
|
|
333
393
|
res.setHeader("x-goog-metadatageneration", `${md.metageneration}`);
|
|
334
394
|
res.setHeader("x-goog-storage-class", md.storageClass);
|
|
@@ -142,20 +142,26 @@ class StorageLayer {
|
|
|
142
142
|
}
|
|
143
143
|
const storedMetadata = this.getMetadata(upload.bucketId, upload.objectId);
|
|
144
144
|
const filePath = this.path(upload.bucketId, upload.objectId);
|
|
145
|
+
function getIncomingMetadata(field) {
|
|
146
|
+
if (!upload.metadata) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const value = upload.metadata[field];
|
|
150
|
+
return value === null ? undefined : value;
|
|
151
|
+
}
|
|
145
152
|
const metadata = new metadata_1.StoredFileMetadata({
|
|
146
153
|
name: upload.objectId,
|
|
147
154
|
bucket: upload.bucketId,
|
|
148
|
-
contentType:
|
|
149
|
-
contentDisposition:
|
|
150
|
-
contentEncoding:
|
|
151
|
-
contentLanguage:
|
|
152
|
-
cacheControl:
|
|
153
|
-
customMetadata:
|
|
155
|
+
contentType: getIncomingMetadata("contentType"),
|
|
156
|
+
contentDisposition: getIncomingMetadata("contentDisposition"),
|
|
157
|
+
contentEncoding: getIncomingMetadata("contentEncoding"),
|
|
158
|
+
contentLanguage: getIncomingMetadata("contentLanguage"),
|
|
159
|
+
cacheControl: getIncomingMetadata("cacheControl"),
|
|
160
|
+
customMetadata: getIncomingMetadata("metadata"),
|
|
154
161
|
}, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
|
|
155
|
-
metadata.update(upload.metadata, false);
|
|
156
162
|
const authorized = await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, {
|
|
157
163
|
before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
|
|
158
|
-
after: metadata
|
|
164
|
+
after: metadata.asRulesResource(),
|
|
159
165
|
}, upload.authorization);
|
|
160
166
|
if (!authorized) {
|
|
161
167
|
this._persistence.deleteFile(upload.path);
|
|
@@ -192,16 +198,20 @@ class StorageLayer {
|
|
|
192
198
|
newMetadata.metadata[k] = "";
|
|
193
199
|
}
|
|
194
200
|
}
|
|
201
|
+
function getMetadata(field) {
|
|
202
|
+
const value = newMetadata[field];
|
|
203
|
+
return value === null ? undefined : value;
|
|
204
|
+
}
|
|
195
205
|
const copiedFileMetadata = new metadata_1.StoredFileMetadata({
|
|
196
206
|
name: destinationObject,
|
|
197
207
|
bucket: destinationBucket,
|
|
198
|
-
contentType:
|
|
199
|
-
contentDisposition:
|
|
200
|
-
contentEncoding:
|
|
201
|
-
contentLanguage:
|
|
202
|
-
cacheControl:
|
|
203
|
-
customMetadata:
|
|
204
|
-
}, this._cloudFunctions, sourceBytes
|
|
208
|
+
contentType: getMetadata("contentType"),
|
|
209
|
+
contentDisposition: getMetadata("contentDisposition"),
|
|
210
|
+
contentEncoding: getMetadata("contentEncoding"),
|
|
211
|
+
contentLanguage: getMetadata("contentLanguage"),
|
|
212
|
+
cacheControl: getMetadata("cacheControl"),
|
|
213
|
+
customMetadata: getMetadata("metadata"),
|
|
214
|
+
}, this._cloudFunctions, sourceBytes);
|
|
205
215
|
const file = new StoredFile(copiedFileMetadata);
|
|
206
216
|
this._files.set(destinationFilePath, file);
|
|
207
217
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
|
|
@@ -7,20 +7,19 @@ const registry_1 = require("../registry");
|
|
|
7
7
|
const types_1 = require("../types");
|
|
8
8
|
const crc_1 = require("./crc");
|
|
9
9
|
class StoredFileMetadata {
|
|
10
|
-
constructor(opts, _cloudFunctions, bytes
|
|
10
|
+
constructor(opts, _cloudFunctions, bytes) {
|
|
11
11
|
this._cloudFunctions = _cloudFunctions;
|
|
12
12
|
this.name = opts.name;
|
|
13
13
|
this.bucket = opts.bucket;
|
|
14
|
-
this.contentType = opts.contentType;
|
|
15
14
|
this.metageneration = opts.metageneration || 1;
|
|
16
15
|
this.generation = opts.generation || Date.now();
|
|
16
|
+
this.contentType = opts.contentType || "application/octet-stream";
|
|
17
17
|
this.storageClass = opts.storageClass || "STANDARD";
|
|
18
|
-
this.contentDisposition = opts.contentDisposition
|
|
19
|
-
this.cacheControl = opts.cacheControl
|
|
18
|
+
this.contentDisposition = opts.contentDisposition;
|
|
19
|
+
this.cacheControl = opts.cacheControl;
|
|
20
20
|
this.contentLanguage = opts.contentLanguage;
|
|
21
21
|
this.customTime = opts.customTime;
|
|
22
|
-
this.contentEncoding = opts.contentEncoding
|
|
23
|
-
this.customMetadata = opts.customMetadata;
|
|
22
|
+
this.contentEncoding = opts.contentEncoding;
|
|
24
23
|
this.downloadTokens = opts.downloadTokens || [];
|
|
25
24
|
if (opts.etag) {
|
|
26
25
|
this.etag = opts.etag;
|
|
@@ -28,6 +27,16 @@ class StoredFileMetadata {
|
|
|
28
27
|
else {
|
|
29
28
|
this.etag = generateETag(this.generation, this.metageneration);
|
|
30
29
|
}
|
|
30
|
+
if (opts.customMetadata) {
|
|
31
|
+
this.customMetadata = {};
|
|
32
|
+
for (const [k, v] of Object.entries(opts.customMetadata)) {
|
|
33
|
+
let stringVal = v;
|
|
34
|
+
if (typeof stringVal !== "string") {
|
|
35
|
+
stringVal = JSON.stringify(v);
|
|
36
|
+
}
|
|
37
|
+
this.customMetadata[k] = stringVal || "";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
this.timeCreated = opts.timeCreated ? new Date(opts.timeCreated) : new Date();
|
|
32
41
|
this.updated = opts.updated ? new Date(opts.updated) : this.timeCreated;
|
|
33
42
|
if (bytes) {
|
|
@@ -43,42 +52,54 @@ class StoredFileMetadata {
|
|
|
43
52
|
else {
|
|
44
53
|
throw new Error("Must pass bytes array or opts object with size, md5hash, and crc32c");
|
|
45
54
|
}
|
|
46
|
-
if (incomingMetadata) {
|
|
47
|
-
this.update(incomingMetadata, false);
|
|
48
|
-
}
|
|
49
55
|
this.deleteFieldsSetAsNull();
|
|
50
56
|
this.setDownloadTokensFromCustomMetadata();
|
|
51
57
|
}
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
clone() {
|
|
59
|
+
const clone = new StoredFileMetadata({
|
|
54
60
|
name: this.name,
|
|
55
61
|
bucket: this.bucket,
|
|
56
62
|
generation: this.generation,
|
|
57
63
|
metageneration: this.metageneration,
|
|
64
|
+
contentType: this.contentType,
|
|
65
|
+
storageClass: this.storageClass,
|
|
58
66
|
size: this.size,
|
|
59
|
-
timeCreated: this.timeCreated,
|
|
60
|
-
updated: this.updated,
|
|
61
67
|
md5Hash: this.md5Hash,
|
|
68
|
+
contentEncoding: this.contentEncoding,
|
|
69
|
+
contentDisposition: this.contentDisposition,
|
|
70
|
+
contentLanguage: this.contentLanguage,
|
|
71
|
+
cacheControl: this.cacheControl,
|
|
72
|
+
customTime: this.customTime,
|
|
62
73
|
crc32c: this.crc32c,
|
|
63
74
|
etag: this.etag,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
downloadTokens: this.downloadTokens,
|
|
76
|
+
customMetadata: this.customMetadata,
|
|
77
|
+
}, this._cloudFunctions);
|
|
78
|
+
clone.timeCreated = this.timeCreated;
|
|
79
|
+
clone.updated = this.updated;
|
|
80
|
+
return clone;
|
|
81
|
+
}
|
|
82
|
+
asRulesResource(proposedChanges) {
|
|
83
|
+
const proposedMetadata = this.clone();
|
|
69
84
|
if (proposedChanges) {
|
|
70
|
-
|
|
71
|
-
rulesResource.generation = Date.now();
|
|
72
|
-
rulesResource.metageneration = 1;
|
|
73
|
-
rulesResource.timeCreated = new Date();
|
|
74
|
-
rulesResource.updated = rulesResource.timeCreated;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
rulesResource.metageneration++;
|
|
78
|
-
}
|
|
79
|
-
rulesResource = Object.assign(Object.assign({}, rulesResource), proposedChanges);
|
|
85
|
+
proposedMetadata.update(proposedChanges, false);
|
|
80
86
|
}
|
|
81
|
-
return
|
|
87
|
+
return {
|
|
88
|
+
name: proposedMetadata.name,
|
|
89
|
+
bucket: proposedMetadata.bucket,
|
|
90
|
+
generation: proposedMetadata.generation,
|
|
91
|
+
metageneration: proposedMetadata.metageneration,
|
|
92
|
+
size: proposedMetadata.size,
|
|
93
|
+
timeCreated: proposedMetadata.timeCreated,
|
|
94
|
+
updated: proposedMetadata.updated,
|
|
95
|
+
md5Hash: proposedMetadata.md5Hash,
|
|
96
|
+
crc32c: proposedMetadata.crc32c,
|
|
97
|
+
etag: proposedMetadata.etag,
|
|
98
|
+
contentDisposition: proposedMetadata.contentDisposition,
|
|
99
|
+
contentEncoding: proposedMetadata.contentEncoding,
|
|
100
|
+
contentType: proposedMetadata.contentType,
|
|
101
|
+
metadata: proposedMetadata.customMetadata || {},
|
|
102
|
+
};
|
|
82
103
|
}
|
|
83
104
|
setDownloadTokensFromCustomMetadata() {
|
|
84
105
|
if (!this.customMetadata) {
|
|
@@ -118,43 +139,52 @@ class StoredFileMetadata {
|
|
|
118
139
|
}
|
|
119
140
|
}
|
|
120
141
|
update(incoming, shouldTrigger = true) {
|
|
121
|
-
if (incoming.contentDisposition) {
|
|
122
|
-
this.contentDisposition =
|
|
142
|
+
if (incoming.contentDisposition !== undefined) {
|
|
143
|
+
this.contentDisposition =
|
|
144
|
+
incoming.contentDisposition === null ? undefined : incoming.contentDisposition;
|
|
123
145
|
}
|
|
124
|
-
if (incoming.contentType) {
|
|
125
|
-
this.contentType = incoming.contentType;
|
|
146
|
+
if (incoming.contentType !== undefined) {
|
|
147
|
+
this.contentType = incoming.contentType === null ? undefined : incoming.contentType;
|
|
126
148
|
}
|
|
127
|
-
if (incoming.
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
this.customMetadata[k] = v === null ? null : String(v);
|
|
131
|
-
}
|
|
149
|
+
if (incoming.contentLanguage !== undefined) {
|
|
150
|
+
this.contentLanguage =
|
|
151
|
+
incoming.contentLanguage === null ? undefined : incoming.contentLanguage;
|
|
132
152
|
}
|
|
133
|
-
if (incoming.
|
|
134
|
-
this.
|
|
153
|
+
if (incoming.contentEncoding !== undefined) {
|
|
154
|
+
this.contentEncoding =
|
|
155
|
+
incoming.contentEncoding === null ? undefined : incoming.contentEncoding;
|
|
135
156
|
}
|
|
136
|
-
if (incoming.
|
|
137
|
-
this.
|
|
157
|
+
if (incoming.cacheControl !== undefined) {
|
|
158
|
+
this.cacheControl = incoming.cacheControl === null ? undefined : incoming.cacheControl;
|
|
138
159
|
}
|
|
139
|
-
if (
|
|
140
|
-
|
|
160
|
+
if (incoming.metadata !== undefined) {
|
|
161
|
+
if (incoming.metadata === null) {
|
|
162
|
+
this.customMetadata = undefined;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.customMetadata = this.customMetadata || {};
|
|
166
|
+
for (const [k, v] of Object.entries(incoming.metadata)) {
|
|
167
|
+
if (v === null) {
|
|
168
|
+
delete this.customMetadata[k];
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.customMetadata[k] = String(v);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (Object.keys(this.customMetadata).length === 0) {
|
|
175
|
+
this.customMetadata = undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
141
178
|
}
|
|
179
|
+
this.metageneration++;
|
|
142
180
|
this.updated = new Date();
|
|
143
|
-
if (incoming.cacheControl) {
|
|
144
|
-
this.cacheControl = incoming.cacheControl;
|
|
145
|
-
}
|
|
146
181
|
this.setDownloadTokensFromCustomMetadata();
|
|
147
|
-
this.deleteFieldsSetAsNull();
|
|
148
182
|
if (shouldTrigger) {
|
|
149
183
|
this._cloudFunctions.dispatch("metadataUpdate", new CloudStorageObjectMetadata(this));
|
|
150
184
|
}
|
|
151
185
|
}
|
|
152
186
|
addDownloadToken(shouldTrigger = true) {
|
|
153
|
-
|
|
154
|
-
this.downloadTokens.push(uuid.v4());
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
this.downloadTokens = [...this.downloadTokens, uuid.v4()];
|
|
187
|
+
this.downloadTokens = [...(this.downloadTokens || []), uuid.v4()];
|
|
158
188
|
this.update({}, shouldTrigger);
|
|
159
189
|
}
|
|
160
190
|
deleteDownloadToken(token) {
|
|
@@ -197,7 +227,7 @@ class OutgoingFirebaseMetadata {
|
|
|
197
227
|
this.crc32c = metadata.crc32c;
|
|
198
228
|
this.etag = metadata.etag;
|
|
199
229
|
this.downloadTokens = metadata.downloadTokens.join(",");
|
|
200
|
-
this.contentEncoding = metadata.contentEncoding;
|
|
230
|
+
this.contentEncoding = metadata.contentEncoding || "identity";
|
|
201
231
|
this.contentDisposition = metadata.contentDisposition;
|
|
202
232
|
this.metadata = metadata.customMetadata;
|
|
203
233
|
this.contentLanguage = metadata.contentLanguage;
|
|
@@ -208,7 +238,7 @@ exports.OutgoingFirebaseMetadata = OutgoingFirebaseMetadata;
|
|
|
208
238
|
class CloudStorageBucketMetadata {
|
|
209
239
|
constructor(id) {
|
|
210
240
|
var _a, _b;
|
|
211
|
-
this.kind = "#
|
|
241
|
+
this.kind = "storage#bucket";
|
|
212
242
|
this.name = id;
|
|
213
243
|
this.id = id;
|
|
214
244
|
this.selfLink = `http://${(_a = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _a === void 0 ? void 0 : _a.host}:${(_b = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _b === void 0 ? void 0 : _b.port}/v1/b/${this.id}`;
|
|
@@ -240,7 +270,7 @@ exports.CloudStorageObjectAccessControlMetadata = CloudStorageObjectAccessContro
|
|
|
240
270
|
class CloudStorageObjectMetadata {
|
|
241
271
|
constructor(metadata) {
|
|
242
272
|
var _a, _b, _c, _d;
|
|
243
|
-
this.kind = "
|
|
273
|
+
this.kind = "storage#object";
|
|
244
274
|
this.name = metadata.name;
|
|
245
275
|
this.bucket = metadata.bucket;
|
|
246
276
|
this.generation = metadata.generation.toString();
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.StorageRulesRuntime = exports.StorageRulesIssues = exports.StorageRulesetInstance = void 0;
|
|
4
4
|
const cross_spawn_1 = require("cross-spawn");
|
|
5
5
|
const error_1 = require("../../../error");
|
|
6
|
+
const AsyncLock = require("async-lock");
|
|
6
7
|
const types_1 = require("./types");
|
|
7
8
|
const jwt = require("jsonwebtoken");
|
|
8
9
|
const emulatorLogger_1 = require("../../emulatorLogger");
|
|
@@ -13,6 +14,8 @@ const constants_1 = require("../../constants");
|
|
|
13
14
|
const download_1 = require("../../download");
|
|
14
15
|
const fs = require("fs-extra");
|
|
15
16
|
const downloadableEmulators_1 = require("../../downloadableEmulators");
|
|
17
|
+
const lock = new AsyncLock();
|
|
18
|
+
const synchonizationKey = "key";
|
|
16
19
|
class StorageRulesetInstance {
|
|
17
20
|
constructor(runtime, rulesVersion, rulesetName) {
|
|
18
21
|
this.runtime = runtime;
|
|
@@ -154,13 +157,18 @@ class StorageRulesRuntime {
|
|
|
154
157
|
throw new error_1.FirebaseError("Attempted to send Cloud Storage rules request with stale id");
|
|
155
158
|
}
|
|
156
159
|
return new Promise((resolve) => {
|
|
157
|
-
var _a, _b;
|
|
158
160
|
this._requests[runtimeActionRequest.id] = {
|
|
159
161
|
request: runtimeActionRequest,
|
|
160
162
|
handler: resolve,
|
|
161
163
|
};
|
|
162
164
|
const serializedRequest = JSON.stringify(runtimeActionRequest);
|
|
163
|
-
(
|
|
165
|
+
lock.acquire(synchonizationKey, (done) => {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
(_b = (_a = this._childprocess) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.write(serializedRequest + "\n");
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
done();
|
|
170
|
+
}, 15);
|
|
171
|
+
});
|
|
164
172
|
});
|
|
165
173
|
}
|
|
166
174
|
async loadRuleset(source) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UploadService = exports.NotCancellableError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
|
|
3
|
+
exports.UploadService = exports.NotCancellableError = exports.UploadPreviouslyFinalizedError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
var UploadType;
|
|
7
7
|
(function (UploadType) {
|
|
8
|
-
UploadType[UploadType["
|
|
9
|
-
UploadType[UploadType["
|
|
8
|
+
UploadType[UploadType["MEDIA"] = 0] = "MEDIA";
|
|
9
|
+
UploadType[UploadType["MULTIPART"] = 1] = "MULTIPART";
|
|
10
|
+
UploadType[UploadType["RESUMABLE"] = 2] = "RESUMABLE";
|
|
10
11
|
})(UploadType = exports.UploadType || (exports.UploadType = {}));
|
|
11
12
|
var UploadStatus;
|
|
12
13
|
(function (UploadStatus) {
|
|
@@ -17,6 +18,9 @@ var UploadStatus;
|
|
|
17
18
|
class UploadNotActiveError extends Error {
|
|
18
19
|
}
|
|
19
20
|
exports.UploadNotActiveError = UploadNotActiveError;
|
|
21
|
+
class UploadPreviouslyFinalizedError extends Error {
|
|
22
|
+
}
|
|
23
|
+
exports.UploadPreviouslyFinalizedError = UploadPreviouslyFinalizedError;
|
|
20
24
|
class NotCancellableError extends Error {
|
|
21
25
|
}
|
|
22
26
|
exports.NotCancellableError = NotCancellableError;
|
|
@@ -28,23 +32,42 @@ class UploadService {
|
|
|
28
32
|
reset() {
|
|
29
33
|
this._uploads = new Map();
|
|
30
34
|
}
|
|
35
|
+
mediaUpload(request) {
|
|
36
|
+
const upload = this.startOneShotUpload({
|
|
37
|
+
bucketId: request.bucketId,
|
|
38
|
+
objectId: request.objectId,
|
|
39
|
+
uploadType: UploadType.MEDIA,
|
|
40
|
+
dataRaw: request.dataRaw,
|
|
41
|
+
authorization: request.authorization,
|
|
42
|
+
});
|
|
43
|
+
this._persistence.deleteFile(upload.path, true);
|
|
44
|
+
this._persistence.appendBytes(upload.path, request.dataRaw);
|
|
45
|
+
return upload;
|
|
46
|
+
}
|
|
31
47
|
multipartUpload(request) {
|
|
32
|
-
const upload = this.
|
|
48
|
+
const upload = this.startOneShotUpload({
|
|
49
|
+
bucketId: request.bucketId,
|
|
50
|
+
objectId: request.objectId,
|
|
51
|
+
uploadType: UploadType.MULTIPART,
|
|
52
|
+
dataRaw: request.dataRaw,
|
|
53
|
+
metadata: JSON.parse(request.metadataRaw),
|
|
54
|
+
authorization: request.authorization,
|
|
55
|
+
});
|
|
33
56
|
this._persistence.deleteFile(upload.path, true);
|
|
34
57
|
this._persistence.appendBytes(upload.path, request.dataRaw);
|
|
35
58
|
return upload;
|
|
36
59
|
}
|
|
37
|
-
|
|
60
|
+
startOneShotUpload(request) {
|
|
38
61
|
const id = (0, uuid_1.v4)();
|
|
39
62
|
const upload = {
|
|
40
|
-
id
|
|
63
|
+
id,
|
|
41
64
|
bucketId: request.bucketId,
|
|
42
65
|
objectId: request.objectId,
|
|
43
|
-
type:
|
|
66
|
+
type: request.uploadType,
|
|
44
67
|
path: this.getStagingFileName(id, request.bucketId, request.objectId),
|
|
45
68
|
status: UploadStatus.FINISHED,
|
|
46
|
-
metadata:
|
|
47
|
-
size:
|
|
69
|
+
metadata: request.metadata,
|
|
70
|
+
size: request.dataRaw.byteLength,
|
|
48
71
|
authorization: request.authorization,
|
|
49
72
|
};
|
|
50
73
|
this._uploads.set(upload.id, upload);
|
|
@@ -94,12 +117,25 @@ class UploadService {
|
|
|
94
117
|
}
|
|
95
118
|
finalizeResumableUpload(uploadId) {
|
|
96
119
|
const upload = this.getResumableUpload(uploadId);
|
|
120
|
+
if (upload.status === UploadStatus.FINISHED) {
|
|
121
|
+
throw new UploadPreviouslyFinalizedError();
|
|
122
|
+
}
|
|
97
123
|
if (upload.status === UploadStatus.CANCELLED) {
|
|
98
124
|
throw new UploadNotActiveError();
|
|
99
125
|
}
|
|
100
126
|
upload.status = UploadStatus.FINISHED;
|
|
101
127
|
return upload;
|
|
102
128
|
}
|
|
129
|
+
setResponseCode(uploadId, code) {
|
|
130
|
+
const upload = this._uploads.get(uploadId);
|
|
131
|
+
if (upload) {
|
|
132
|
+
upload.prevResponseCode = code;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
getPreviousResponseCode(uploadId) {
|
|
136
|
+
var _a;
|
|
137
|
+
return ((_a = this._uploads.get(uploadId)) === null || _a === void 0 ? void 0 : _a.prevResponseCode) || 200;
|
|
138
|
+
}
|
|
103
139
|
getStagingFileName(uploadId, bucketId, objectId) {
|
|
104
140
|
return encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
|
|
105
141
|
}
|
|
@@ -350,7 +350,7 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
350
350
|
if (extension &&
|
|
351
351
|
extension.latestVersion &&
|
|
352
352
|
semver.lt(extensionSpec.version, extension.latestVersion)) {
|
|
353
|
-
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) is lower than the current version (${clc.bold(extension.latestVersion)}) for the extension '${clc.bold(`${args.publisherId}/${args.extensionId}`)}'. Please make sure this version is greater than the current version (${clc.bold(extension.latestVersion)}) inside of extension.yaml.\n
|
|
353
|
+
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) is lower than the current version (${clc.bold(extension.latestVersion)}) for the extension '${clc.bold(`${args.publisherId}/${args.extensionId}`)}'. Please make sure this version is greater than the current version (${clc.bold(extension.latestVersion)}) inside of extension.yaml.\n`, { exit: 104 });
|
|
354
354
|
}
|
|
355
355
|
else if (extension &&
|
|
356
356
|
extension.latestVersion &&
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BLOCKING_EVENT_TO_LABEL_KEY = exports.BLOCKING_LABEL_KEY_TO_EVENT = exports.BLOCKING_LABEL = exports.HASH_LABEL = exports.CODEBASE_LABEL = void 0;
|
|
4
|
+
exports.CODEBASE_LABEL = "firebase-functions-codebase";
|
|
5
|
+
exports.HASH_LABEL = "firebase-functions-hash";
|
|
6
|
+
exports.BLOCKING_LABEL = "deployment-blocking";
|
|
7
|
+
exports.BLOCKING_LABEL_KEY_TO_EVENT = {
|
|
8
|
+
"before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
|
|
9
|
+
"before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
|
|
10
|
+
};
|
|
11
|
+
exports.BLOCKING_EVENT_TO_LABEL_KEY = {
|
|
12
|
+
"providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
|
|
13
|
+
"providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
|
|
14
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
|
|
3
|
+
exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
|
|
4
4
|
exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
5
5
|
exports.STORAGE_EVENTS = [
|
|
6
6
|
"google.cloud.storage.object.v1.finalized",
|
|
@@ -15,3 +15,4 @@ exports.DATABASE_EVENTS = [
|
|
|
15
15
|
"google.firebase.database.ref.v1.updated",
|
|
16
16
|
"google.firebase.database.ref.v1.deleted",
|
|
17
17
|
];
|
|
18
|
+
exports.REMOTE_CONFIG_EVENT = "google.firebase.remoteconfig.remoteConfig.v1.updated";
|