@dereekb/firebase-server 13.0.0 → 13.0.1
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/index.cjs.default.js +1 -0
- package/index.cjs.js +1923 -2001
- package/index.cjs.mjs +2 -0
- package/index.esm.js +1924 -2000
- package/mailgun/index.cjs.default.js +1 -0
- package/mailgun/index.cjs.js +28 -35
- package/mailgun/index.cjs.mjs +2 -0
- package/mailgun/index.esm.js +28 -35
- package/mailgun/package.json +17 -22
- package/model/index.cjs.default.js +1 -0
- package/model/index.cjs.js +4042 -4573
- package/model/index.cjs.mjs +2 -0
- package/model/index.esm.js +4042 -4573
- package/model/package.json +23 -27
- package/package.json +42 -61
- package/src/lib/function/error.d.ts +0 -8
- package/test/index.cjs.default.js +1 -0
- package/test/index.cjs.js +867 -933
- package/test/index.cjs.mjs +2 -0
- package/test/index.esm.js +867 -933
- package/test/package.json +22 -26
- package/zoho/index.cjs.default.js +1 -0
- package/zoho/index.cjs.js +82 -85
- package/zoho/index.cjs.mjs +2 -0
- package/zoho/index.esm.js +82 -85
- package/zoho/package.json +17 -21
package/index.cjs.js
CHANGED
|
@@ -5,8 +5,6 @@ var util = require('@dereekb/util');
|
|
|
5
5
|
var https = require('firebase-functions/https');
|
|
6
6
|
var date = require('@dereekb/date');
|
|
7
7
|
var makeError = require('make-error');
|
|
8
|
-
require('core-js/modules/es.iterator.constructor.js');
|
|
9
|
-
require('core-js/modules/es.iterator.for-each.js');
|
|
10
8
|
var rxjs = require('rxjs');
|
|
11
9
|
var firestore = require('@google-cloud/firestore');
|
|
12
10
|
var common = require('@nestjs/common');
|
|
@@ -14,152 +12,141 @@ var nestjs = require('@dereekb/nestjs');
|
|
|
14
12
|
var v2 = require('firebase-functions/v2');
|
|
15
13
|
var model = require('@dereekb/model');
|
|
16
14
|
var admin = require('firebase-admin');
|
|
17
|
-
require('core-js/modules/es.iterator.map.js');
|
|
18
15
|
var storage = require('@google-cloud/storage');
|
|
19
16
|
var dateFns = require('date-fns');
|
|
20
17
|
var types = require('util/types');
|
|
21
|
-
require('core-js/modules/es.map.get-or-insert.js');
|
|
22
|
-
require('core-js/modules/es.map.get-or-insert-computed.js');
|
|
23
18
|
var core = require('@nestjs/core');
|
|
24
19
|
var platformExpress = require('@nestjs/platform-express');
|
|
25
20
|
var express = require('express');
|
|
26
21
|
|
|
27
|
-
/**
|
|
28
|
-
* @deprecated use DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE instead
|
|
29
|
-
*/
|
|
30
|
-
const NO_AUTH_ERROR_CODE = firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE;
|
|
31
22
|
function unauthenticatedContextHasNoAuthData() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
return unauthenticatedError({
|
|
24
|
+
message: 'expected auth',
|
|
25
|
+
code: firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE
|
|
26
|
+
});
|
|
36
27
|
}
|
|
37
|
-
/**
|
|
38
|
-
* @deprecated use DBX_FIREBASE_SERVER_NO_UID_ERROR_CODE instead
|
|
39
|
-
*/
|
|
40
|
-
const NO_UID_ERROR_CODE = firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE;
|
|
41
28
|
function unauthenticatedContextHasNoUidError() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
return unauthenticatedError({
|
|
30
|
+
message: 'no user uid',
|
|
31
|
+
code: firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE
|
|
32
|
+
});
|
|
46
33
|
}
|
|
47
34
|
// MARK: General Errors
|
|
48
35
|
const UNAUTHENTICATED_ERROR_CODE = 'UNAUTHENTICATED';
|
|
49
36
|
function unauthenticatedError(messageOrError) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
const serverError = util.partialServerError(messageOrError);
|
|
38
|
+
return new https.HttpsError('unauthenticated', serverError?.message || 'unauthenticated', {
|
|
39
|
+
status: 401,
|
|
40
|
+
code: UNAUTHENTICATED_ERROR_CODE,
|
|
41
|
+
...serverError,
|
|
42
|
+
_error: undefined
|
|
43
|
+
});
|
|
57
44
|
}
|
|
58
45
|
const FORBIDDEN_ERROR_CODE = 'FORBIDDEN';
|
|
59
46
|
function forbiddenError(messageOrError) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
const serverError = util.partialServerError(messageOrError);
|
|
48
|
+
return new https.HttpsError('permission-denied', serverError?.message || 'forbidden', {
|
|
49
|
+
status: 403,
|
|
50
|
+
code: FORBIDDEN_ERROR_CODE,
|
|
51
|
+
...serverError,
|
|
52
|
+
_error: undefined
|
|
53
|
+
});
|
|
67
54
|
}
|
|
68
55
|
const PERMISSION_DENIED_ERROR_CODE = 'PERMISSION_DENIED';
|
|
69
56
|
function permissionDeniedError(messageOrError) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
const serverError = util.partialServerError(messageOrError);
|
|
58
|
+
return new https.HttpsError('permission-denied', serverError?.message || 'permission denied', {
|
|
59
|
+
status: 403,
|
|
60
|
+
code: PERMISSION_DENIED_ERROR_CODE,
|
|
61
|
+
...serverError,
|
|
62
|
+
_error: undefined
|
|
63
|
+
});
|
|
77
64
|
}
|
|
78
65
|
const NOT_FOUND_ERROR_CODE = 'NOT_FOUND';
|
|
79
66
|
function notFoundError(messageOrError) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
67
|
+
const serverError = util.partialServerError(messageOrError);
|
|
68
|
+
return new https.HttpsError('not-found', serverError?.message || 'not found', {
|
|
69
|
+
status: 404,
|
|
70
|
+
code: NOT_FOUND_ERROR_CODE,
|
|
71
|
+
...serverError,
|
|
72
|
+
_error: undefined
|
|
73
|
+
});
|
|
87
74
|
}
|
|
88
75
|
const MODEL_NOT_AVAILABLE_ERROR_CODE = 'MODEL_NOT_AVAILABLE';
|
|
89
76
|
function modelNotAvailableError(messageOrError) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
77
|
+
const serverError = util.partialServerError(messageOrError);
|
|
78
|
+
return new https.HttpsError('not-found', serverError?.message || 'model was not available', {
|
|
79
|
+
status: 404,
|
|
80
|
+
code: MODEL_NOT_AVAILABLE_ERROR_CODE,
|
|
81
|
+
...serverError,
|
|
82
|
+
_error: undefined
|
|
83
|
+
});
|
|
97
84
|
}
|
|
98
85
|
const BAD_REQUEST_ERROR_CODE = 'BAD_REQUEST';
|
|
99
86
|
function badRequestError(messageOrError) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
87
|
+
const serverError = util.partialServerError(messageOrError);
|
|
88
|
+
return new https.HttpsError('invalid-argument', serverError?.message || 'bad request', {
|
|
89
|
+
status: 400,
|
|
90
|
+
code: BAD_REQUEST_ERROR_CODE,
|
|
91
|
+
...serverError,
|
|
92
|
+
_error: undefined
|
|
93
|
+
});
|
|
107
94
|
}
|
|
108
95
|
const CONFLICT_ERROR_CODE = 'CONFLICT';
|
|
109
96
|
function preconditionConflictError(messageOrError) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
const serverError = util.partialServerError(messageOrError);
|
|
98
|
+
return new https.HttpsError('failed-precondition', serverError?.message || 'conflict', {
|
|
99
|
+
status: 409,
|
|
100
|
+
code: CONFLICT_ERROR_CODE,
|
|
101
|
+
...serverError,
|
|
102
|
+
_error: undefined
|
|
103
|
+
});
|
|
117
104
|
}
|
|
118
105
|
const ALREADY_EXISTS_ERROR_CODE = 'ALREADY_EXISTS';
|
|
119
106
|
function alreadyExistsError(messageOrError) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
107
|
+
const serverError = util.partialServerError(messageOrError);
|
|
108
|
+
return new https.HttpsError('already-exists', serverError?.message || 'already exists', {
|
|
109
|
+
status: 409,
|
|
110
|
+
code: ALREADY_EXISTS_ERROR_CODE,
|
|
111
|
+
...serverError,
|
|
112
|
+
_error: undefined
|
|
113
|
+
});
|
|
127
114
|
}
|
|
128
115
|
const UNAVAILABLE_ERROR_CODE = 'UNAVAILABLE';
|
|
129
116
|
function unavailableError(messageOrError) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
const serverError = util.partialServerError(messageOrError);
|
|
118
|
+
return new https.HttpsError('unavailable', serverError?.message || 'service unavailable', {
|
|
119
|
+
status: 503,
|
|
120
|
+
code: UNAVAILABLE_ERROR_CODE,
|
|
121
|
+
...serverError,
|
|
122
|
+
_error: undefined
|
|
123
|
+
});
|
|
137
124
|
}
|
|
138
125
|
const UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE = 'UNAVAILABLE_OR_DEACTIVATED_FUNCTION';
|
|
139
126
|
function unavailableOrDeactivatedFunctionError(messageOrError) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
127
|
+
const serverError = util.partialServerError(messageOrError);
|
|
128
|
+
return new https.HttpsError('unimplemented', serverError?.message || 'the requested function is not available or has been deactivated for use', {
|
|
129
|
+
status: 501,
|
|
130
|
+
code: UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE,
|
|
131
|
+
...serverError,
|
|
132
|
+
_error: undefined
|
|
133
|
+
});
|
|
147
134
|
}
|
|
148
135
|
const INTERNAL_SERVER_ERROR_CODE = 'INTERNAL_ERROR';
|
|
149
136
|
function internalServerError(messageOrError) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
137
|
+
const serverError = util.partialServerError(messageOrError);
|
|
138
|
+
return new https.HttpsError('internal', serverError?.message || 'internal error', {
|
|
139
|
+
status: 500,
|
|
140
|
+
code: INTERNAL_SERVER_ERROR_CODE,
|
|
141
|
+
...serverError,
|
|
142
|
+
_error: undefined
|
|
143
|
+
});
|
|
157
144
|
}
|
|
158
145
|
function isFirebaseHttpsError(input) {
|
|
159
|
-
|
|
146
|
+
return typeof input === 'object' && input.code != null && input.httpErrorCode != null && input.toJSON != null;
|
|
160
147
|
}
|
|
161
148
|
function isFirebaseError(input) {
|
|
162
|
-
|
|
149
|
+
return typeof input === 'object' && input.code != null && input.message != null && input.toJSON != null;
|
|
163
150
|
}
|
|
164
151
|
/**
|
|
165
152
|
* Creates a FirebaseServerErrorInfo from the input.
|
|
@@ -168,73 +155,74 @@ function isFirebaseError(input) {
|
|
|
168
155
|
* @returns
|
|
169
156
|
*/
|
|
170
157
|
function firebaseServerErrorInfo(e) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
158
|
+
let type = 'unknown';
|
|
159
|
+
let httpsError;
|
|
160
|
+
let firebaseError;
|
|
161
|
+
let firebaseErrorCode;
|
|
162
|
+
let httpsErrorDetailsServerError;
|
|
163
|
+
let serverErrorCode;
|
|
164
|
+
if (e != null) {
|
|
165
|
+
if (isFirebaseHttpsError(e)) {
|
|
166
|
+
type = 'httpsError';
|
|
167
|
+
httpsError = e;
|
|
168
|
+
firebaseErrorCode = httpsError.code;
|
|
169
|
+
if (httpsError.details && util.isServerError(httpsError.details)) {
|
|
170
|
+
httpsErrorDetailsServerError = httpsError.details;
|
|
171
|
+
serverErrorCode = httpsErrorDetailsServerError.code;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (isFirebaseError(e)) {
|
|
175
|
+
type = 'firebaseError';
|
|
176
|
+
firebaseError = e;
|
|
177
|
+
firebaseErrorCode = firebaseError.code;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
httpsError,
|
|
182
|
+
firebaseError,
|
|
183
|
+
firebaseErrorCode,
|
|
184
|
+
httpsErrorDetailsServerError,
|
|
185
|
+
serverErrorCode,
|
|
186
|
+
type,
|
|
187
|
+
e
|
|
188
|
+
};
|
|
201
189
|
}
|
|
202
190
|
function firebaseServerErrorInfoCodePair(e) {
|
|
203
|
-
|
|
204
|
-
|
|
191
|
+
const info = firebaseServerErrorInfo(e);
|
|
192
|
+
return [info.firebaseErrorCode, info];
|
|
205
193
|
}
|
|
206
194
|
function firebaseServerErrorInfoServerErrorPair(e) {
|
|
207
|
-
|
|
208
|
-
|
|
195
|
+
const info = firebaseServerErrorInfo(e);
|
|
196
|
+
return [info.httpsErrorDetailsServerError, info];
|
|
209
197
|
}
|
|
210
198
|
function firebaseServerErrorInfoServerErrorCodePair(e) {
|
|
211
|
-
|
|
212
|
-
|
|
199
|
+
const info = firebaseServerErrorInfo(e);
|
|
200
|
+
return [info.serverErrorCode, info];
|
|
213
201
|
}
|
|
214
202
|
function handleFirebaseError(e, handleFirebaseErrorFn) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
const firebaseError = e.code ? e : undefined;
|
|
204
|
+
if (firebaseError) {
|
|
205
|
+
handleFirebaseErrorFn(firebaseError);
|
|
206
|
+
}
|
|
219
207
|
}
|
|
220
208
|
|
|
221
209
|
function isContextWithAuthData(context) {
|
|
222
|
-
|
|
210
|
+
return Boolean(context.auth !== null && context.auth?.uid);
|
|
223
211
|
}
|
|
224
212
|
function assertIsContextWithAuthData(context) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
213
|
+
if (!isContextWithAuthData(context)) {
|
|
214
|
+
throw unauthenticatedContextHasNoAuthData();
|
|
215
|
+
}
|
|
228
216
|
}
|
|
229
217
|
|
|
230
218
|
function firebaseAuthTokenFromDecodedIdToken(token) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
219
|
+
return {
|
|
220
|
+
email: token.email,
|
|
221
|
+
emailVerified: token.email_verified,
|
|
222
|
+
phoneNumber: token.phone_number,
|
|
223
|
+
lastSignInTime: new Date(token.auth_time).toISOString(),
|
|
224
|
+
lastRefreshTime: new Date(token.iat).toISOString()
|
|
225
|
+
};
|
|
238
226
|
}
|
|
239
227
|
|
|
240
228
|
/**
|
|
@@ -243,483 +231,458 @@ function firebaseAuthTokenFromDecodedIdToken(token) {
|
|
|
243
231
|
* @returns
|
|
244
232
|
*/
|
|
245
233
|
async function getAuthUserOrUndefined(promise) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
234
|
+
try {
|
|
235
|
+
return await promise;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
if (error?.code === firebase.FIREBASE_AUTH_USER_NOT_FOUND_ERROR) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
253
244
|
}
|
|
254
|
-
}
|
|
255
245
|
}
|
|
256
246
|
|
|
257
247
|
/**
|
|
258
248
|
* Thrown by sendSetupDetails() if the user has no setup configuration available, meaning they probably already have accepted their invite or is in an invalid state.
|
|
259
249
|
*/
|
|
260
250
|
class FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError extends makeError.BaseError {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
251
|
+
constructor() {
|
|
252
|
+
super(`This user has no setup configuration available.`);
|
|
253
|
+
}
|
|
264
254
|
}
|
|
265
255
|
/**
|
|
266
256
|
* Thrown by sendSetupDetails() if the user was recently sent details.
|
|
267
257
|
*/
|
|
268
258
|
class FirebaseServerAuthNewUserSendSetupDetailsThrottleError extends makeError.BaseError {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
259
|
+
lastSentAt;
|
|
260
|
+
constructor(lastSentAt) {
|
|
261
|
+
super(`This user was recently sent details. Try again later.`);
|
|
262
|
+
this.lastSentAt = lastSentAt;
|
|
263
|
+
}
|
|
274
264
|
}
|
|
275
265
|
/**
|
|
276
266
|
* Thrown by sendSetupDetails() if the user was recently sent details.
|
|
277
267
|
*/
|
|
278
268
|
class FirebaseServerAuthNewUserSendSetupDetailsSendOnceError extends makeError.BaseError {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
269
|
+
constructor() {
|
|
270
|
+
super(`The user has been sent details before and the sendSetupDetailsOnce configuration was true.`);
|
|
271
|
+
}
|
|
282
272
|
}
|
|
283
273
|
|
|
284
|
-
const DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR = util.randomNumberFactory({
|
|
285
|
-
min: 100000,
|
|
286
|
-
max: 1000000,
|
|
287
|
-
round: 'floor'
|
|
288
|
-
}); // 6 digits
|
|
274
|
+
const DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR = util.randomNumberFactory({ min: 100000, max: 1000000, round: 'floor' }); // 6 digits
|
|
289
275
|
class AbstractFirebaseServerAuthUserContext {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
});
|
|
419
|
-
}
|
|
276
|
+
_service;
|
|
277
|
+
_uid;
|
|
278
|
+
_loadRecord = util.cachedGetter(() => this._service.auth.getUser(this._uid));
|
|
279
|
+
constructor(service, uid) {
|
|
280
|
+
this._service = service;
|
|
281
|
+
this._uid = uid;
|
|
282
|
+
}
|
|
283
|
+
get service() {
|
|
284
|
+
return this._service;
|
|
285
|
+
}
|
|
286
|
+
get uid() {
|
|
287
|
+
return this._uid;
|
|
288
|
+
}
|
|
289
|
+
async exists() {
|
|
290
|
+
return getAuthUserOrUndefined(this._loadRecord()).then((x) => Boolean(x));
|
|
291
|
+
}
|
|
292
|
+
loadRecord() {
|
|
293
|
+
return this._loadRecord();
|
|
294
|
+
}
|
|
295
|
+
loadDetails() {
|
|
296
|
+
return this.loadRecord().then((record) => this.service.authDetailsForRecord(record));
|
|
297
|
+
}
|
|
298
|
+
_generateResetPasswordKey() {
|
|
299
|
+
return String(DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR());
|
|
300
|
+
}
|
|
301
|
+
async beginResetPassword() {
|
|
302
|
+
const password = this._generateResetPasswordKey();
|
|
303
|
+
const passwordClaimsData = {
|
|
304
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY]: password,
|
|
305
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY]: date.toISODateString(new Date())
|
|
306
|
+
};
|
|
307
|
+
// set the claims
|
|
308
|
+
await this.updateClaims(passwordClaimsData);
|
|
309
|
+
// update the user
|
|
310
|
+
await this.updateUser({ password });
|
|
311
|
+
return passwordClaimsData;
|
|
312
|
+
}
|
|
313
|
+
async loadResetPasswordClaims() {
|
|
314
|
+
const claims = await this.loadClaims();
|
|
315
|
+
if (claims.resetPassword != null) {
|
|
316
|
+
return claims;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Sets the user's password.
|
|
324
|
+
*/
|
|
325
|
+
async setPassword(password) {
|
|
326
|
+
const record = await this.updateUser({ password });
|
|
327
|
+
// clear password reset claims
|
|
328
|
+
await this.updateClaims({
|
|
329
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY]: null,
|
|
330
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY]: null
|
|
331
|
+
});
|
|
332
|
+
return record;
|
|
333
|
+
}
|
|
334
|
+
async updateUser(template) {
|
|
335
|
+
return this.service.auth.updateUser(this.uid, template);
|
|
336
|
+
}
|
|
337
|
+
async loadRoles() {
|
|
338
|
+
const claims = await this.loadClaims();
|
|
339
|
+
return this.service.readRoles(claims);
|
|
340
|
+
}
|
|
341
|
+
async addRoles(roles) {
|
|
342
|
+
const claims = this._claimsForRolesChange(roles);
|
|
343
|
+
return this.updateClaims(claims);
|
|
344
|
+
}
|
|
345
|
+
async removeRoles(roles) {
|
|
346
|
+
const baseClaims = this._claimsForRolesChange(roles);
|
|
347
|
+
const claims = {};
|
|
348
|
+
util.forEachKeyValue(baseClaims, {
|
|
349
|
+
forEach: ([key]) => {
|
|
350
|
+
claims[key] = null; // set null on every key
|
|
351
|
+
},
|
|
352
|
+
filter: util.KeyValueTypleValueFilter.NONE // don't skip any key/value
|
|
353
|
+
});
|
|
354
|
+
return this.updateClaims(claims);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Sets the claims using the input roles and roles set.
|
|
358
|
+
*
|
|
359
|
+
* All other claims are cleared.
|
|
360
|
+
*
|
|
361
|
+
* Use the claimsToRetain input to retain other claims that are outside of the roles.
|
|
362
|
+
*
|
|
363
|
+
* @param roles
|
|
364
|
+
* @param claimsToRetain
|
|
365
|
+
* @returns
|
|
366
|
+
*/
|
|
367
|
+
async setRoles(roles, claimsToRetain) {
|
|
368
|
+
const claims = {
|
|
369
|
+
...claimsToRetain,
|
|
370
|
+
...this._claimsForRolesChange(Array.from(roles))
|
|
371
|
+
};
|
|
372
|
+
return this.setClaims(claims);
|
|
373
|
+
}
|
|
374
|
+
_claimsForRolesChange(roles) {
|
|
375
|
+
// filter null/undefined since the claims will contain null values for claims that are not related.
|
|
376
|
+
return util.filterNullAndUndefinedValues(this.service.claimsForRoles(util.asSet(roles)));
|
|
377
|
+
}
|
|
378
|
+
loadClaims() {
|
|
379
|
+
return this.loadRecord().then((x) => (x.customClaims ?? {}));
|
|
380
|
+
}
|
|
381
|
+
async updateClaims(claims) {
|
|
382
|
+
const currentClaims = await this.loadClaims();
|
|
383
|
+
let newClaims;
|
|
384
|
+
if (currentClaims) {
|
|
385
|
+
newClaims = {
|
|
386
|
+
...currentClaims,
|
|
387
|
+
...util.filterUndefinedValues(claims, false)
|
|
388
|
+
};
|
|
389
|
+
newClaims = util.filterNullAndUndefinedValues(newClaims);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
newClaims = claims;
|
|
393
|
+
}
|
|
394
|
+
return this.setClaims(newClaims);
|
|
395
|
+
}
|
|
396
|
+
clearClaims() {
|
|
397
|
+
return this.setClaims(null);
|
|
398
|
+
}
|
|
399
|
+
setClaims(claims) {
|
|
400
|
+
return this.service.auth.setCustomUserClaims(this.uid, claims).then(() => {
|
|
401
|
+
this._loadRecord.reset(); // reset the cache
|
|
402
|
+
});
|
|
403
|
+
}
|
|
420
404
|
}
|
|
421
405
|
class AbstractFirebaseServerAuthContext {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
406
|
+
_service;
|
|
407
|
+
_context;
|
|
408
|
+
_authRoles = util.cachedGetter(() => this.service.readRoles(this.claims));
|
|
409
|
+
_isAdmin = util.cachedGetter(() => this.service.isAdminInRoles(this._authRoles()));
|
|
410
|
+
_hasSignedTos = util.cachedGetter(() => this.service.hasSignedTosInRoles(this._authRoles()));
|
|
411
|
+
_userContext = util.cachedGetter(() => this.service.userContext(this.context.auth.uid));
|
|
412
|
+
constructor(service, context) {
|
|
413
|
+
this._service = service;
|
|
414
|
+
this._context = context;
|
|
415
|
+
}
|
|
416
|
+
get service() {
|
|
417
|
+
return this._service;
|
|
418
|
+
}
|
|
419
|
+
get context() {
|
|
420
|
+
return this._context;
|
|
421
|
+
}
|
|
422
|
+
get userContext() {
|
|
423
|
+
return this._userContext();
|
|
424
|
+
}
|
|
425
|
+
get isAdmin() {
|
|
426
|
+
return this._isAdmin();
|
|
427
|
+
}
|
|
428
|
+
get hasSignedTos() {
|
|
429
|
+
return this._hasSignedTos();
|
|
430
|
+
}
|
|
431
|
+
get authRoles() {
|
|
432
|
+
return this._authRoles();
|
|
433
|
+
}
|
|
434
|
+
get token() {
|
|
435
|
+
return this.context.auth.token;
|
|
436
|
+
}
|
|
437
|
+
get claims() {
|
|
438
|
+
return this.context.auth.token;
|
|
439
|
+
}
|
|
440
|
+
// MARK: FirebaseServerAuthUserContext
|
|
441
|
+
get uid() {
|
|
442
|
+
return this.userContext.uid;
|
|
443
|
+
}
|
|
460
444
|
}
|
|
461
445
|
/**
|
|
462
446
|
* 1 hour
|
|
463
447
|
*/
|
|
464
448
|
const DEFAULT_SETUP_COM_THROTTLE_TIME = date.hoursToMs(1);
|
|
465
449
|
function userContextFromUid(authService, userContextOrUid) {
|
|
466
|
-
|
|
467
|
-
|
|
450
|
+
const userContext = typeof userContextOrUid === 'string' ? authService.userContext(userContextOrUid) : userContextOrUid;
|
|
451
|
+
return userContext;
|
|
468
452
|
}
|
|
469
453
|
class AbstractFirebaseServerNewUserService {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const user = await this.authService.auth.createUser({
|
|
628
|
-
uid,
|
|
629
|
-
displayName,
|
|
630
|
-
email,
|
|
631
|
-
phoneNumber,
|
|
632
|
-
password
|
|
633
|
-
});
|
|
634
|
-
return {
|
|
635
|
-
user,
|
|
636
|
-
password
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
generateRandomSetupPassword() {
|
|
640
|
-
return `${DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR()}`;
|
|
641
|
-
}
|
|
642
|
-
async updateClaimsToClearUser(userContext) {
|
|
643
|
-
await userContext.updateClaims({
|
|
644
|
-
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY]: null,
|
|
645
|
-
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_LAST_COM_DATE_KEY]: null
|
|
646
|
-
});
|
|
647
|
-
}
|
|
454
|
+
_authService;
|
|
455
|
+
setupThrottleTime = DEFAULT_SETUP_COM_THROTTLE_TIME;
|
|
456
|
+
constructor(authService) {
|
|
457
|
+
this._authService = authService;
|
|
458
|
+
}
|
|
459
|
+
get authService() {
|
|
460
|
+
return this._authService;
|
|
461
|
+
}
|
|
462
|
+
async initializeNewUser(input) {
|
|
463
|
+
const { uid, email, phone, sendSetupContent, sendSetupContentIfUserExists, sendSetupDetailsOnce, sendSetupIgnoreThrottle, sendSetupThrowErrors, data, sendDetailsInTestEnvironment } = input;
|
|
464
|
+
let userRecordPromise;
|
|
465
|
+
if (uid) {
|
|
466
|
+
userRecordPromise = this.authService.auth.getUser(uid);
|
|
467
|
+
}
|
|
468
|
+
else if (email) {
|
|
469
|
+
userRecordPromise = this.authService.auth.getUserByEmail(email);
|
|
470
|
+
}
|
|
471
|
+
else if (phone) {
|
|
472
|
+
userRecordPromise = this.authService.auth.getUserByPhoneNumber(phone);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
throw new Error('email or phone is required to initialize a new user.');
|
|
476
|
+
}
|
|
477
|
+
let userRecord = await getAuthUserOrUndefined(userRecordPromise);
|
|
478
|
+
let userRecordId;
|
|
479
|
+
let createdUser = false;
|
|
480
|
+
if (!userRecord) {
|
|
481
|
+
const createResult = await this.createNewUser(input);
|
|
482
|
+
// add the setup password to the user's credentials
|
|
483
|
+
const userContext = this.authService.userContext(createResult.user.uid);
|
|
484
|
+
await this.addNewUserSetupClaims(userContext, createResult.password);
|
|
485
|
+
createdUser = true;
|
|
486
|
+
userRecordId = userContext.uid;
|
|
487
|
+
userRecord = await userContext.loadRecord();
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
userRecordId = userRecord.uid;
|
|
491
|
+
}
|
|
492
|
+
// send content if necessary
|
|
493
|
+
if ((createdUser && sendSetupContent === true) || sendSetupContentIfUserExists) {
|
|
494
|
+
const sentEmail = await this.sendSetupContent(userRecordId, { data, sendSetupDetailsOnce, ignoreSendThrottleTime: sendSetupIgnoreThrottle, throwErrors: sendSetupThrowErrors, sendDetailsInTestEnvironment });
|
|
495
|
+
// reload the user record
|
|
496
|
+
if (sentEmail) {
|
|
497
|
+
const userContext = this.authService.userContext(userRecordId);
|
|
498
|
+
userRecord = await userContext.loadRecord();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return userRecord;
|
|
502
|
+
}
|
|
503
|
+
async addNewUserSetupClaims(userContextOrUid, setupPassword) {
|
|
504
|
+
const password = setupPassword ?? this.generateRandomSetupPassword();
|
|
505
|
+
const userContext = userContextFromUid(this.authService, userContextOrUid);
|
|
506
|
+
await userContext.updateClaims({
|
|
507
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY]: password
|
|
508
|
+
});
|
|
509
|
+
return userContext;
|
|
510
|
+
}
|
|
511
|
+
async sendSetupContent(userContextOrUid, config) {
|
|
512
|
+
const setupDetails = await this.loadSetupDetails(userContextOrUid, config);
|
|
513
|
+
let sentContent = false;
|
|
514
|
+
if (setupDetails) {
|
|
515
|
+
const { setupCommunicationAt } = setupDetails.claims;
|
|
516
|
+
const hasSentCommunication = Boolean(setupCommunicationAt);
|
|
517
|
+
if (config?.sendSetupDetailsOnce && hasSentCommunication) {
|
|
518
|
+
// do not send.
|
|
519
|
+
if (config?.throwErrors) {
|
|
520
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsSendOnceError();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
const lastSentAt = setupCommunicationAt ? new Date(setupCommunicationAt) : undefined;
|
|
525
|
+
const sendIsThrottled = hasSentCommunication && !config?.ignoreSendThrottleTime && util.isThrottled(this.setupThrottleTime, lastSentAt);
|
|
526
|
+
if (!sendIsThrottled) {
|
|
527
|
+
await this.sendSetupContentToUser(setupDetails);
|
|
528
|
+
await this.updateSetupContentSentTime(setupDetails);
|
|
529
|
+
sentContent = true;
|
|
530
|
+
}
|
|
531
|
+
else if (config?.throwErrors) {
|
|
532
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsThrottleError(lastSentAt);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (config?.throwErrors) {
|
|
537
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError();
|
|
538
|
+
}
|
|
539
|
+
return sentContent;
|
|
540
|
+
}
|
|
541
|
+
async loadSetupDetails(userContextOrUid, config) {
|
|
542
|
+
const userContext = userContextFromUid(this.authService, userContextOrUid);
|
|
543
|
+
const userExists = await userContext.exists();
|
|
544
|
+
let details;
|
|
545
|
+
if (userExists) {
|
|
546
|
+
details = await this.loadSetupDetailsForUserContext(userContext, config);
|
|
547
|
+
}
|
|
548
|
+
return details;
|
|
549
|
+
}
|
|
550
|
+
async loadSetupDetailsForUserContext(userContext, config) {
|
|
551
|
+
let details;
|
|
552
|
+
const { setupPassword, setupCommunicationAt } = await userContext.loadClaims();
|
|
553
|
+
if (setupPassword) {
|
|
554
|
+
details = {
|
|
555
|
+
userContext,
|
|
556
|
+
claims: {
|
|
557
|
+
setupPassword,
|
|
558
|
+
setupCommunicationAt
|
|
559
|
+
},
|
|
560
|
+
data: config?.data,
|
|
561
|
+
sendDetailsInTestEnvironment: config?.sendDetailsInTestEnvironment
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return details;
|
|
565
|
+
}
|
|
566
|
+
async updateSetupContentSentTime(details) {
|
|
567
|
+
const setupCommunicationAt = date.toISODateString(new Date());
|
|
568
|
+
await details.userContext.updateClaims({
|
|
569
|
+
setupCommunicationAt
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Update a user's claims to clear any setup-related content.
|
|
574
|
+
*
|
|
575
|
+
* Returns true if a user was updated.
|
|
576
|
+
*
|
|
577
|
+
* @param uid
|
|
578
|
+
*/
|
|
579
|
+
async markUserSetupAsComplete(uid) {
|
|
580
|
+
const userContext = this.authService.userContext(uid);
|
|
581
|
+
const userExists = await userContext.exists();
|
|
582
|
+
if (userExists) {
|
|
583
|
+
await this.updateClaimsToClearUser(userContext);
|
|
584
|
+
}
|
|
585
|
+
return userExists;
|
|
586
|
+
}
|
|
587
|
+
async createNewUser(input) {
|
|
588
|
+
const { uid, displayName, email, phone: phoneNumber, setupPassword: inputPassword } = input;
|
|
589
|
+
const password = inputPassword ?? this.generateRandomSetupPassword();
|
|
590
|
+
const user = await this.authService.auth.createUser({
|
|
591
|
+
uid,
|
|
592
|
+
displayName,
|
|
593
|
+
email,
|
|
594
|
+
phoneNumber,
|
|
595
|
+
password
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
user,
|
|
599
|
+
password
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
generateRandomSetupPassword() {
|
|
603
|
+
return `${DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR()}`;
|
|
604
|
+
}
|
|
605
|
+
async updateClaimsToClearUser(userContext) {
|
|
606
|
+
await userContext.updateClaims({
|
|
607
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY]: null,
|
|
608
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_LAST_COM_DATE_KEY]: null
|
|
609
|
+
});
|
|
610
|
+
}
|
|
648
611
|
}
|
|
649
612
|
class NoSetupContentFirebaseServerNewUserService extends AbstractFirebaseServerNewUserService {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
613
|
+
async sendSetupContentToUser(user) {
|
|
614
|
+
// send nothing.
|
|
615
|
+
}
|
|
653
616
|
}
|
|
654
617
|
/**
|
|
655
618
|
* FirebaseServer auth service that provides accessors to auth-related components.
|
|
656
619
|
*/
|
|
657
|
-
class FirebaseServerAuthService {
|
|
620
|
+
class FirebaseServerAuthService {
|
|
621
|
+
}
|
|
658
622
|
/**
|
|
659
623
|
* Abstract FirebaseServerAuthService implementation.
|
|
660
624
|
*/
|
|
661
625
|
class AbstractFirebaseServerAuthService {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
};
|
|
719
|
-
}
|
|
626
|
+
_auth;
|
|
627
|
+
constructor(auth) {
|
|
628
|
+
this._auth = auth;
|
|
629
|
+
}
|
|
630
|
+
get auth() {
|
|
631
|
+
return this._auth;
|
|
632
|
+
}
|
|
633
|
+
context(context) {
|
|
634
|
+
assertIsContextWithAuthData(context);
|
|
635
|
+
return this._context(context);
|
|
636
|
+
}
|
|
637
|
+
isAdmin(claims) {
|
|
638
|
+
return this.isAdminInRoles(this.readRoles(claims));
|
|
639
|
+
}
|
|
640
|
+
isAdminInRoles(roles) {
|
|
641
|
+
return roles.has(util.AUTH_ADMIN_ROLE);
|
|
642
|
+
}
|
|
643
|
+
hasSignedTos(claims) {
|
|
644
|
+
return this.hasSignedTosInRoles(this.readRoles(claims));
|
|
645
|
+
}
|
|
646
|
+
hasSignedTosInRoles(roles) {
|
|
647
|
+
return roles.has(util.AUTH_TOS_SIGNED_ROLE);
|
|
648
|
+
}
|
|
649
|
+
newUser() {
|
|
650
|
+
return new NoSetupContentFirebaseServerNewUserService(this);
|
|
651
|
+
}
|
|
652
|
+
authContextInfo(context) {
|
|
653
|
+
const { auth } = context;
|
|
654
|
+
let result;
|
|
655
|
+
if (auth) {
|
|
656
|
+
const _roles = util.cachedGetter(() => this.readRoles(auth.token));
|
|
657
|
+
const getClaims = () => auth.token;
|
|
658
|
+
result = {
|
|
659
|
+
uid: auth.uid,
|
|
660
|
+
isAdmin: () => this.isAdminInRoles(_roles()),
|
|
661
|
+
getClaims,
|
|
662
|
+
getAuthRoles: _roles,
|
|
663
|
+
token: firebaseAuthTokenFromDecodedIdToken(auth.token)
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
authDetailsForRecord(record) {
|
|
669
|
+
return {
|
|
670
|
+
uid: record.uid,
|
|
671
|
+
email: record.email,
|
|
672
|
+
emailVerified: record.emailVerified,
|
|
673
|
+
phoneNumber: record.phoneNumber,
|
|
674
|
+
disabled: record.disabled,
|
|
675
|
+
displayName: record.displayName,
|
|
676
|
+
photoURL: record.photoURL,
|
|
677
|
+
creationTime: record.metadata.creationTime ? new Date(record.metadata.creationTime).toISOString() : undefined,
|
|
678
|
+
lastSignInTime: record.metadata.lastSignInTime ? new Date(record.metadata.lastSignInTime).toISOString() : undefined,
|
|
679
|
+
lastRefreshTime: record.metadata.lastRefreshTime ? new Date(record.metadata.lastRefreshTime).toISOString() : undefined
|
|
680
|
+
};
|
|
681
|
+
}
|
|
720
682
|
}
|
|
721
683
|
|
|
722
|
-
class FirebaseServerEnvService {
|
|
684
|
+
class FirebaseServerEnvService {
|
|
685
|
+
}
|
|
723
686
|
|
|
724
687
|
/**
|
|
725
688
|
* Creates UpdateData corresponding to the input increment update.
|
|
@@ -728,9 +691,9 @@ class FirebaseServerEnvService {}
|
|
|
728
691
|
* @returns
|
|
729
692
|
*/
|
|
730
693
|
function firestoreServerIncrementUpdateToUpdateData(input) {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
694
|
+
return util.mapObjectMap(input, (incrementValue) => {
|
|
695
|
+
return firestore.FieldValue.increment(incrementValue ?? 0);
|
|
696
|
+
});
|
|
734
697
|
}
|
|
735
698
|
|
|
736
699
|
/**
|
|
@@ -740,26 +703,26 @@ function firestoreServerIncrementUpdateToUpdateData(input) {
|
|
|
740
703
|
* @returns
|
|
741
704
|
*/
|
|
742
705
|
function firestoreServerArrayUpdateToUpdateData(input) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
let result;
|
|
747
|
-
if (fieldUpdate) {
|
|
748
|
-
result = util.mapObjectMap(fieldUpdate, arrayUpdate => {
|
|
706
|
+
const union = input?.union;
|
|
707
|
+
const remove = input?.remove;
|
|
708
|
+
function createUpdatesWithArrayFunction(fieldUpdate, arrayUpdateFunction) {
|
|
749
709
|
let result;
|
|
750
|
-
if (
|
|
751
|
-
|
|
710
|
+
if (fieldUpdate) {
|
|
711
|
+
result = util.mapObjectMap(fieldUpdate, (arrayUpdate) => {
|
|
712
|
+
let result;
|
|
713
|
+
if (arrayUpdate) {
|
|
714
|
+
result = arrayUpdateFunction(...arrayUpdate); // use spread operator to insert each value as an argument, as "nested arrays" are not allowed in the Firestore
|
|
715
|
+
}
|
|
716
|
+
return result;
|
|
717
|
+
});
|
|
752
718
|
}
|
|
753
719
|
return result;
|
|
754
|
-
});
|
|
755
720
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
};
|
|
762
|
-
return updateData;
|
|
721
|
+
const updateData = {
|
|
722
|
+
...createUpdatesWithArrayFunction(union, firestore.FieldValue.arrayUnion),
|
|
723
|
+
...createUpdatesWithArrayFunction(remove, firestore.FieldValue.arrayRemove)
|
|
724
|
+
};
|
|
725
|
+
return updateData;
|
|
763
726
|
}
|
|
764
727
|
|
|
765
728
|
// MARK: Accessor
|
|
@@ -767,53 +730,54 @@ function firestoreServerArrayUpdateToUpdateData(input) {
|
|
|
767
730
|
* FirestoreDocumentDataAccessor implementation for a batch.
|
|
768
731
|
*/
|
|
769
732
|
class WriteBatchFirestoreDocumentDataAccessor {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
733
|
+
documentRef;
|
|
734
|
+
_batch;
|
|
735
|
+
constructor(batch, documentRef) {
|
|
736
|
+
this.documentRef = documentRef;
|
|
737
|
+
this._batch = batch;
|
|
738
|
+
}
|
|
739
|
+
get batch() {
|
|
740
|
+
return this._batch;
|
|
741
|
+
}
|
|
742
|
+
stream() {
|
|
743
|
+
return rxjs.from(this.get()); // todo
|
|
744
|
+
}
|
|
745
|
+
create(data) {
|
|
746
|
+
this.batch.create(this.documentRef, data);
|
|
747
|
+
return Promise.resolve();
|
|
748
|
+
}
|
|
749
|
+
exists() {
|
|
750
|
+
return this.get().then((x) => x.exists);
|
|
751
|
+
}
|
|
752
|
+
get() {
|
|
753
|
+
return this.documentRef.get();
|
|
754
|
+
}
|
|
755
|
+
getWithConverter(converter) {
|
|
756
|
+
return this.documentRef.withConverter(converter).get();
|
|
757
|
+
}
|
|
758
|
+
delete(params) {
|
|
759
|
+
this.batch.delete(this.documentRef, params?.precondition);
|
|
760
|
+
return Promise.resolve();
|
|
761
|
+
}
|
|
762
|
+
set(data) {
|
|
763
|
+
this.batch.set(this.documentRef, data);
|
|
764
|
+
return Promise.resolve();
|
|
765
|
+
}
|
|
766
|
+
increment(data, params) {
|
|
767
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
768
|
+
}
|
|
769
|
+
arrayUpdate(data, params) {
|
|
770
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
771
|
+
}
|
|
772
|
+
update(data, params) {
|
|
773
|
+
if (params?.precondition != null) {
|
|
774
|
+
this.batch.update(this.documentRef, data, params?.precondition);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
this.batch.update(this.documentRef, data);
|
|
778
|
+
}
|
|
779
|
+
return Promise.resolve();
|
|
780
|
+
}
|
|
817
781
|
}
|
|
818
782
|
/**
|
|
819
783
|
* Creates a new FirestoreDocumentDataAccessorFactory for a Batch.
|
|
@@ -822,81 +786,78 @@ class WriteBatchFirestoreDocumentDataAccessor {
|
|
|
822
786
|
* @returns
|
|
823
787
|
*/
|
|
824
788
|
function writeBatchAccessorFactory(writeBatch) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
789
|
+
return {
|
|
790
|
+
accessorFor: (ref) => new WriteBatchFirestoreDocumentDataAccessor(writeBatch, ref)
|
|
791
|
+
};
|
|
828
792
|
}
|
|
829
793
|
// MARK: Context
|
|
830
794
|
class WriteBatchFirestoreDocumentContext {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
795
|
+
_batch;
|
|
796
|
+
contextType = firebase.FirestoreDocumentContextType.BATCH;
|
|
797
|
+
accessorFactory;
|
|
798
|
+
constructor(batch) {
|
|
799
|
+
this._batch = batch;
|
|
800
|
+
this.accessorFactory = writeBatchAccessorFactory(batch);
|
|
801
|
+
}
|
|
802
|
+
get batch() {
|
|
803
|
+
return this._batch;
|
|
804
|
+
}
|
|
841
805
|
}
|
|
842
806
|
function writeBatchDocumentContext(batch) {
|
|
843
|
-
|
|
807
|
+
return new WriteBatchFirestoreDocumentContext(batch);
|
|
844
808
|
}
|
|
845
809
|
|
|
846
810
|
// MARK: Accessor
|
|
847
811
|
class DefaultFirestoreDocumentDataAccessor {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
update(data, params) {
|
|
886
|
-
return params?.precondition ? this.documentRef.update(data, params.precondition) : this.documentRef.update(data);
|
|
887
|
-
}
|
|
812
|
+
_documentRef;
|
|
813
|
+
constructor(documentRef) {
|
|
814
|
+
this._documentRef = documentRef;
|
|
815
|
+
}
|
|
816
|
+
get documentRef() {
|
|
817
|
+
return this._documentRef;
|
|
818
|
+
}
|
|
819
|
+
stream() {
|
|
820
|
+
return firebase.streamFromOnSnapshot(({ next, error }) => this.documentRef.onSnapshot(next, error));
|
|
821
|
+
}
|
|
822
|
+
create(data) {
|
|
823
|
+
return this.documentRef.create(data);
|
|
824
|
+
}
|
|
825
|
+
exists() {
|
|
826
|
+
return this.get().then((x) => x.exists);
|
|
827
|
+
}
|
|
828
|
+
get() {
|
|
829
|
+
return this.documentRef.get();
|
|
830
|
+
}
|
|
831
|
+
getWithConverter(converter) {
|
|
832
|
+
return this.documentRef.withConverter(converter).get();
|
|
833
|
+
}
|
|
834
|
+
delete(params) {
|
|
835
|
+
return this.documentRef.delete(params?.precondition);
|
|
836
|
+
}
|
|
837
|
+
set(data, options) {
|
|
838
|
+
return options ? this.documentRef.set(data, options) : this.documentRef.set(data);
|
|
839
|
+
}
|
|
840
|
+
increment(data, params) {
|
|
841
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
842
|
+
}
|
|
843
|
+
arrayUpdate(data, params) {
|
|
844
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
845
|
+
}
|
|
846
|
+
update(data, params) {
|
|
847
|
+
return params?.precondition ? this.documentRef.update(data, params.precondition) : this.documentRef.update(data);
|
|
848
|
+
}
|
|
888
849
|
}
|
|
889
850
|
function defaultFirestoreAccessorFactory() {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
851
|
+
return {
|
|
852
|
+
accessorFor: (ref) => new DefaultFirestoreDocumentDataAccessor(ref)
|
|
853
|
+
};
|
|
893
854
|
}
|
|
894
855
|
// MARK: Context
|
|
895
856
|
function defaultFirestoreDocumentContext() {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
857
|
+
return {
|
|
858
|
+
contextType: firebase.FirestoreDocumentContextType.NONE,
|
|
859
|
+
accessorFactory: defaultFirestoreAccessorFactory()
|
|
860
|
+
};
|
|
900
861
|
}
|
|
901
862
|
|
|
902
863
|
// MARK: Accessor
|
|
@@ -904,56 +865,57 @@ function defaultFirestoreDocumentContext() {
|
|
|
904
865
|
* FirestoreDocumentDataAccessor implementation for a transaction.
|
|
905
866
|
*/
|
|
906
867
|
class TransactionFirestoreDocumentDataAccessor {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
868
|
+
_transaction;
|
|
869
|
+
_documentRef;
|
|
870
|
+
constructor(transaction, documentRef) {
|
|
871
|
+
this._transaction = transaction;
|
|
872
|
+
this._documentRef = documentRef;
|
|
873
|
+
}
|
|
874
|
+
get transaction() {
|
|
875
|
+
return this._transaction;
|
|
876
|
+
}
|
|
877
|
+
get documentRef() {
|
|
878
|
+
return this._documentRef;
|
|
879
|
+
}
|
|
880
|
+
stream() {
|
|
881
|
+
return rxjs.from(this.get());
|
|
882
|
+
}
|
|
883
|
+
create(data) {
|
|
884
|
+
this.transaction.create(this.documentRef, data);
|
|
885
|
+
return Promise.resolve();
|
|
886
|
+
}
|
|
887
|
+
exists() {
|
|
888
|
+
return this.get().then((x) => x.exists);
|
|
889
|
+
}
|
|
890
|
+
get() {
|
|
891
|
+
return this.transaction.get(this.documentRef);
|
|
892
|
+
}
|
|
893
|
+
getWithConverter(converter) {
|
|
894
|
+
return this.transaction.get(this.documentRef.withConverter(converter));
|
|
895
|
+
}
|
|
896
|
+
delete() {
|
|
897
|
+
this.transaction.delete(this.documentRef);
|
|
898
|
+
return Promise.resolve();
|
|
899
|
+
}
|
|
900
|
+
set(data, options) {
|
|
901
|
+
this.transaction.set(this.documentRef, data, options);
|
|
902
|
+
return Promise.resolve();
|
|
903
|
+
}
|
|
904
|
+
increment(data, params) {
|
|
905
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
906
|
+
}
|
|
907
|
+
arrayUpdate(data, params) {
|
|
908
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
909
|
+
}
|
|
910
|
+
update(data, params) {
|
|
911
|
+
if (params?.precondition) {
|
|
912
|
+
this.transaction.update(this.documentRef, data, params?.precondition);
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
this.transaction.update(this.documentRef, data);
|
|
916
|
+
}
|
|
917
|
+
return Promise.resolve();
|
|
918
|
+
}
|
|
957
919
|
}
|
|
958
920
|
/**
|
|
959
921
|
* Creates a new FirestoreDocumentDataAccessorFactory for a Transaction.
|
|
@@ -962,122 +924,123 @@ class TransactionFirestoreDocumentDataAccessor {
|
|
|
962
924
|
* @returns
|
|
963
925
|
*/
|
|
964
926
|
function transactionAccessorFactory(transaction) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
927
|
+
return {
|
|
928
|
+
accessorFor: (ref) => new TransactionFirestoreDocumentDataAccessor(transaction, ref)
|
|
929
|
+
};
|
|
968
930
|
}
|
|
969
931
|
// MARK: Context
|
|
970
932
|
class TransactionFirestoreDocumentContext {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
933
|
+
_transaction;
|
|
934
|
+
contextType = firebase.FirestoreDocumentContextType.TRANSACTION;
|
|
935
|
+
accessorFactory;
|
|
936
|
+
constructor(transaction) {
|
|
937
|
+
this._transaction = transaction;
|
|
938
|
+
this.accessorFactory = transactionAccessorFactory(transaction);
|
|
939
|
+
}
|
|
940
|
+
get transaction() {
|
|
941
|
+
return this._transaction;
|
|
942
|
+
}
|
|
981
943
|
}
|
|
982
944
|
function transactionDocumentContext(transaction) {
|
|
983
|
-
|
|
945
|
+
return new TransactionFirestoreDocumentContext(transaction);
|
|
984
946
|
}
|
|
985
947
|
|
|
986
948
|
function collectionRefForPath(start, path, pathSegments) {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
949
|
+
let ref = start.collection(path);
|
|
950
|
+
if (pathSegments?.length) {
|
|
951
|
+
if (pathSegments?.length % 2 !== 0) {
|
|
952
|
+
throw new Error(`Invalid number of path segments provided for collection. Path: "${path}" + "${pathSegments}"`);
|
|
953
|
+
}
|
|
954
|
+
const batches = util.batch(pathSegments, 2); // batch to tuple [string, string]
|
|
955
|
+
batches.forEach((x) => {
|
|
956
|
+
const [first, second] = x;
|
|
957
|
+
ref = ref.doc(first).collection(second);
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
return ref;
|
|
999
961
|
}
|
|
1000
962
|
function docRefForPath(start, path, pathSegments) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
963
|
+
let doc = (path ? start.doc(path) : start.doc());
|
|
964
|
+
if (pathSegments?.length) {
|
|
965
|
+
const batches = util.batch(pathSegments, 2); // batch to tuple [string, string]
|
|
966
|
+
batches.forEach((x) => {
|
|
967
|
+
const [first, second] = x;
|
|
968
|
+
const collection = doc.collection(first);
|
|
969
|
+
doc = second ? collection.doc(second) : collection.doc();
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
return doc;
|
|
1011
973
|
}
|
|
1012
974
|
function googleCloudFirestoreAccessorDriver() {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
975
|
+
return {
|
|
976
|
+
doc: (collection, path, ...pathSegments) => docRefForPath(collection, path, pathSegments),
|
|
977
|
+
docAtPath: (firestore, fullPath) => firestore.doc(fullPath),
|
|
978
|
+
collectionGroup: (firestore, collectionId) => firestore.collectionGroup(collectionId),
|
|
979
|
+
collection: (firestore, path, ...pathSegments) => collectionRefForPath(firestore, path, pathSegments),
|
|
980
|
+
subcollection: (document, path, ...pathSegments) => collectionRefForPath(document, path, pathSegments),
|
|
981
|
+
transactionFactoryForFirestore: (firestore) => async (fn) => await firestore.runTransaction(fn),
|
|
982
|
+
writeBatchFactoryForFirestore: (firestore) => () => firestore.batch(),
|
|
983
|
+
defaultContextFactory: defaultFirestoreDocumentContext,
|
|
984
|
+
transactionContextFactory: transactionDocumentContext,
|
|
985
|
+
writeBatchContextFactory: writeBatchDocumentContext
|
|
986
|
+
};
|
|
1025
987
|
}
|
|
1026
988
|
|
|
1027
989
|
const FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING = {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
990
|
+
[firebase.FIRESTORE_LIMIT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.limit(data.limit),
|
|
991
|
+
[firebase.FIRESTORE_LIMIT_TO_LAST_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.limitToLast(data.limit),
|
|
992
|
+
[firebase.FIRESTORE_ORDER_BY_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.orderBy(data.fieldPath, data.directionStr),
|
|
993
|
+
[firebase.FIRESTORE_ORDER_BY_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.orderBy(firestore.FieldPath.documentId(), data.directionStr),
|
|
994
|
+
[firebase.FIRESTORE_WHERE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.where(data.fieldPath, data.opStr, data.value),
|
|
995
|
+
[firebase.FIRESTORE_WHERE_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.where(firestore.FieldPath.documentId(), data.opStr, data.value),
|
|
996
|
+
[firebase.FIRESTORE_OFFSET_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.offset(data.offset),
|
|
997
|
+
[firebase.FIRESTORE_START_AT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAt(data.snapshot),
|
|
998
|
+
[firebase.FIRESTORE_START_AT_VALUE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAt(...data.fieldValues),
|
|
999
|
+
[firebase.FIRESTORE_START_AFTER_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAfter(data.snapshot),
|
|
1000
|
+
[firebase.FIRESTORE_END_AT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endAt(data.snapshot),
|
|
1001
|
+
[firebase.FIRESTORE_END_AT_VALUE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endAt(...data.fieldValues),
|
|
1002
|
+
[firebase.FIRESTORE_END_BEFORE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endBefore(data.snapshot)
|
|
1041
1003
|
};
|
|
1042
1004
|
function firestoreClientQueryConstraintFunctionsDriver() {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1005
|
+
return firebase.makeFirestoreQueryConstraintFunctionsDriver({
|
|
1006
|
+
mapping: FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING,
|
|
1007
|
+
init: (query) => query,
|
|
1008
|
+
build: (query) => query,
|
|
1009
|
+
documentIdFieldPath: () => firestore.FieldPath.documentId()
|
|
1010
|
+
});
|
|
1049
1011
|
}
|
|
1050
1012
|
function googleCloudFirestoreQueryDriver() {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1013
|
+
return {
|
|
1014
|
+
...firestoreClientQueryConstraintFunctionsDriver(),
|
|
1015
|
+
countDocs(query) {
|
|
1016
|
+
return query
|
|
1017
|
+
.count()
|
|
1018
|
+
.get()
|
|
1019
|
+
.then((x) => x.data().count);
|
|
1020
|
+
},
|
|
1021
|
+
getDocs(query, transaction) {
|
|
1022
|
+
let result;
|
|
1023
|
+
if (transaction) {
|
|
1024
|
+
result = transaction.get(query);
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
result = query.get();
|
|
1028
|
+
}
|
|
1029
|
+
return result;
|
|
1030
|
+
},
|
|
1031
|
+
streamDocs(query) {
|
|
1032
|
+
return firebase.streamFromOnSnapshot(({ next, error }) => query.onSnapshot(next, error));
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1072
1035
|
}
|
|
1073
1036
|
|
|
1074
1037
|
function googleCloudFirestoreDrivers() {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1038
|
+
return {
|
|
1039
|
+
firestoreDriverIdentifier: '@google-cloud/firestore',
|
|
1040
|
+
firestoreDriverType: 'production',
|
|
1041
|
+
firestoreAccessorDriver: googleCloudFirestoreAccessorDriver(),
|
|
1042
|
+
firestoreQueryDriver: googleCloudFirestoreQueryDriver()
|
|
1043
|
+
};
|
|
1081
1044
|
}
|
|
1082
1045
|
|
|
1083
1046
|
/**
|
|
@@ -1086,9 +1049,9 @@ function googleCloudFirestoreDrivers() {
|
|
|
1086
1049
|
const googleCloudFirestoreContextFactory = firebase.firestoreContextFactory(googleCloudFirestoreDrivers());
|
|
1087
1050
|
|
|
1088
1051
|
function assertContextHasAuth(context) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1052
|
+
if (!isContextWithAuthData(context)) {
|
|
1053
|
+
throw unauthenticatedContextHasNoUidError();
|
|
1054
|
+
}
|
|
1092
1055
|
}
|
|
1093
1056
|
/**
|
|
1094
1057
|
* Attempts to load data from the document. A modelNotAvailableError is thrown if the snapshot data is null/undefined (the document does not exist).
|
|
@@ -1098,13 +1061,13 @@ function assertContextHasAuth(context) {
|
|
|
1098
1061
|
* @returns
|
|
1099
1062
|
*/
|
|
1100
1063
|
async function assertSnapshotData(document, message) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1064
|
+
const data = await document.snapshotData();
|
|
1065
|
+
if (data == null) {
|
|
1066
|
+
throw modelNotAvailableError({
|
|
1067
|
+
message: message ?? `The ${document.modelType} was unavailable.`
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
return data;
|
|
1108
1071
|
}
|
|
1109
1072
|
/**
|
|
1110
1073
|
* Convenience function for assertSnapshotData that also attaches the id and key of the document to the data.
|
|
@@ -1114,8 +1077,8 @@ async function assertSnapshotData(document, message) {
|
|
|
1114
1077
|
* @returns
|
|
1115
1078
|
*/
|
|
1116
1079
|
async function assertSnapshotDataWithKey(document, message) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1080
|
+
const data = await assertSnapshotData(document, message);
|
|
1081
|
+
return firebase.setIdAndKeyFromKeyIdRefOnDocumentData(data, document);
|
|
1119
1082
|
}
|
|
1120
1083
|
/**
|
|
1121
1084
|
* Asserts that the document exists. A modelNotAvailableError is thrown if the document does not exist.
|
|
@@ -1125,10 +1088,10 @@ async function assertSnapshotDataWithKey(document, message) {
|
|
|
1125
1088
|
* @returns
|
|
1126
1089
|
*/
|
|
1127
1090
|
async function assertDocumentExists(document, message) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1091
|
+
const exists = await document.exists();
|
|
1092
|
+
if (!exists) {
|
|
1093
|
+
throw documentModelNotAvailableError(document, message);
|
|
1094
|
+
}
|
|
1132
1095
|
}
|
|
1133
1096
|
/**
|
|
1134
1097
|
* Error thrown by assertDocumentExists().
|
|
@@ -1138,28 +1101,28 @@ async function assertDocumentExists(document, message) {
|
|
|
1138
1101
|
* @returns
|
|
1139
1102
|
*/
|
|
1140
1103
|
function documentModelNotAvailableError(document, message) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1104
|
+
return modelNotAvailableError({
|
|
1105
|
+
message: message ?? `The ${document.modelType} was unavailable.`
|
|
1106
|
+
});
|
|
1144
1107
|
}
|
|
1145
1108
|
|
|
1146
1109
|
const PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE = 'PHONE_NUMBER_ALREADY_EXISTS';
|
|
1147
1110
|
function phoneNumberAlreadyExistsError() {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1111
|
+
return preconditionConflictError({
|
|
1112
|
+
code: PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE,
|
|
1113
|
+
message: 'This phone number already exists in the system.'
|
|
1114
|
+
});
|
|
1152
1115
|
}
|
|
1153
1116
|
function handleFirebaseAuthError(e, handleUnknownCode) {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1117
|
+
handleFirebaseError(e, (firebaseError) => {
|
|
1118
|
+
switch (firebaseError.code) {
|
|
1119
|
+
case 'auth/phone-number-already-exists':
|
|
1120
|
+
throw phoneNumberAlreadyExistsError();
|
|
1121
|
+
default:
|
|
1122
|
+
handleUnknownCode?.(firebaseError);
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1163
1126
|
}
|
|
1164
1127
|
|
|
1165
1128
|
/******************************************************************************
|
|
@@ -1206,10 +1169,10 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
1206
1169
|
const FIREBASE_APP_TOKEN = 'FIREBASE_APP_TOKEN';
|
|
1207
1170
|
// MARK: Firebase Admin Provider
|
|
1208
1171
|
function firebaseServerAppTokenProvider(useFactory) {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1172
|
+
return {
|
|
1173
|
+
provide: FIREBASE_APP_TOKEN,
|
|
1174
|
+
useFactory
|
|
1175
|
+
};
|
|
1213
1176
|
}
|
|
1214
1177
|
|
|
1215
1178
|
// MARK: Tokens
|
|
@@ -1220,23 +1183,31 @@ const FIREBASE_AUTH_TOKEN = 'FIREBASE_AUTH_TOKEN';
|
|
|
1220
1183
|
/**
|
|
1221
1184
|
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
1222
1185
|
*/
|
|
1223
|
-
exports.FirebaseServerAuthModule = class FirebaseServerAuthModule {
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1186
|
+
exports.FirebaseServerAuthModule = class FirebaseServerAuthModule {
|
|
1187
|
+
};
|
|
1188
|
+
exports.FirebaseServerAuthModule = __decorate([
|
|
1189
|
+
common.Module({
|
|
1190
|
+
providers: [
|
|
1191
|
+
{
|
|
1192
|
+
provide: FIREBASE_AUTH_TOKEN,
|
|
1193
|
+
useFactory: (app) => app.auth(),
|
|
1194
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
1195
|
+
}
|
|
1196
|
+
],
|
|
1197
|
+
exports: [FIREBASE_AUTH_TOKEN]
|
|
1198
|
+
})
|
|
1199
|
+
], exports.FirebaseServerAuthModule);
|
|
1232
1200
|
function provideFirebaseServerAuthService(provider) {
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1201
|
+
return [
|
|
1202
|
+
{
|
|
1203
|
+
...provider,
|
|
1204
|
+
inject: provider.inject ?? [FIREBASE_AUTH_TOKEN]
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
provide: FirebaseServerAuthService,
|
|
1208
|
+
useExisting: provider.provide
|
|
1209
|
+
}
|
|
1210
|
+
];
|
|
1240
1211
|
}
|
|
1241
1212
|
/**
|
|
1242
1213
|
* Convenience function used to generate ModuleMetadata for an app's Auth related modules and FirebaseServerAuthService provider.
|
|
@@ -1246,58 +1217,58 @@ function provideFirebaseServerAuthService(provider) {
|
|
|
1246
1217
|
* @returns
|
|
1247
1218
|
*/
|
|
1248
1219
|
function firebaseServerAuthModuleMetadata(config) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1220
|
+
return nestjs.mergeModuleMetadata({
|
|
1221
|
+
imports: [exports.FirebaseServerAuthModule],
|
|
1222
|
+
exports: [exports.FirebaseServerAuthModule, config.serviceProvider.provide],
|
|
1223
|
+
providers: provideFirebaseServerAuthService(config.serviceProvider)
|
|
1224
|
+
}, config);
|
|
1254
1225
|
}
|
|
1255
1226
|
|
|
1256
1227
|
function assertIsAdminInRequest(request) {
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1228
|
+
if (!isAdminInRequest(request)) {
|
|
1229
|
+
throw forbiddenError();
|
|
1230
|
+
}
|
|
1260
1231
|
}
|
|
1261
1232
|
function isAdminInRequest(request) {
|
|
1262
|
-
|
|
1233
|
+
return request.nest.authService.context(request).isAdmin;
|
|
1263
1234
|
}
|
|
1264
1235
|
function assertIsAdminOrTargetUserInRequestData(request, requireUid) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1236
|
+
if (!isAdminOrTargetUserInRequestData(request, requireUid)) {
|
|
1237
|
+
throw forbiddenError();
|
|
1238
|
+
}
|
|
1239
|
+
return request.data.uid ?? request.auth?.uid;
|
|
1269
1240
|
}
|
|
1270
1241
|
function isAdminOrTargetUserInRequestData(request, requireUid = false) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1242
|
+
const uid = request.data.uid;
|
|
1243
|
+
const authUid = request.auth?.uid;
|
|
1244
|
+
let isAdminOrTargetUser = true;
|
|
1245
|
+
if ((requireUid && uid == null) || (uid != null && uid !== authUid)) {
|
|
1246
|
+
isAdminOrTargetUser = request.nest.authService.context(request).isAdmin;
|
|
1247
|
+
}
|
|
1248
|
+
return isAdminOrTargetUser;
|
|
1278
1249
|
}
|
|
1279
1250
|
function assertHasSignedTosInRequest(request) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1251
|
+
if (!hasSignedTosInRequest(request)) {
|
|
1252
|
+
throw forbiddenError({
|
|
1253
|
+
message: 'ToS has not been signed.'
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1285
1256
|
}
|
|
1286
1257
|
function hasSignedTosInRequest(request) {
|
|
1287
|
-
|
|
1258
|
+
return request.nest.authService.context(request).hasSignedTos;
|
|
1288
1259
|
}
|
|
1289
1260
|
function assertHasRolesInRequest(request, authRoles) {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1261
|
+
if (!hasAuthRolesInRequest(request, authRoles)) {
|
|
1262
|
+
throw forbiddenError({
|
|
1263
|
+
message: 'Missing required auth roles.',
|
|
1264
|
+
data: {
|
|
1265
|
+
roles: util.asArray(authRoles)
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1298
1269
|
}
|
|
1299
1270
|
function hasAuthRolesInRequest(request, authRoles) {
|
|
1300
|
-
|
|
1271
|
+
return util.containsAllValues(request.nest.authService.context(request).authRoles, authRoles);
|
|
1301
1272
|
}
|
|
1302
1273
|
/**
|
|
1303
1274
|
* Returns true if the claims have a FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY claims value, indicating they are a newly invited user.
|
|
@@ -1307,8 +1278,8 @@ function hasAuthRolesInRequest(request, authRoles) {
|
|
|
1307
1278
|
* @param request
|
|
1308
1279
|
*/
|
|
1309
1280
|
function hasNewUserSetupPasswordInRequest(request) {
|
|
1310
|
-
|
|
1311
|
-
|
|
1281
|
+
const claims = request.nest.authService.context(request).claims;
|
|
1282
|
+
return claims[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY] != null;
|
|
1312
1283
|
}
|
|
1313
1284
|
|
|
1314
1285
|
/**
|
|
@@ -1318,123 +1289,114 @@ function hasNewUserSetupPasswordInRequest(request) {
|
|
|
1318
1289
|
* @returns
|
|
1319
1290
|
*/
|
|
1320
1291
|
function onCallDevelopmentFunction(map, config = {}) {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
} else {
|
|
1338
|
-
throw developmentUnknownSpecifierError(specifier);
|
|
1339
|
-
}
|
|
1340
|
-
};
|
|
1292
|
+
const { preAssert = () => undefined } = config;
|
|
1293
|
+
return (request) => {
|
|
1294
|
+
const specifier = request.data.specifier;
|
|
1295
|
+
const devFn = map[specifier];
|
|
1296
|
+
if (devFn) {
|
|
1297
|
+
preAssert({ request, specifier });
|
|
1298
|
+
return devFn({
|
|
1299
|
+
...request,
|
|
1300
|
+
specifier,
|
|
1301
|
+
data: request.data.data
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
else {
|
|
1305
|
+
throw developmentUnknownSpecifierError(specifier);
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1341
1308
|
}
|
|
1342
1309
|
function developmentUnknownSpecifierError(specifier) {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1310
|
+
return badRequestError(util.serverError({
|
|
1311
|
+
status: 400,
|
|
1312
|
+
code: 'UNKNOWN_SPECIFIER_ERROR',
|
|
1313
|
+
message: `Invalid specifier "${specifier}" to run.`,
|
|
1314
|
+
data: {
|
|
1315
|
+
specifier
|
|
1316
|
+
}
|
|
1317
|
+
}));
|
|
1351
1318
|
}
|
|
1352
1319
|
|
|
1353
1320
|
const NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE = 'NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION';
|
|
1354
1321
|
function noRunNameSpecifiedForScheduledFunctionDevelopmentFunction() {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1322
|
+
return badRequestError({
|
|
1323
|
+
code: NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE,
|
|
1324
|
+
message: `Must specify run parameter.`
|
|
1325
|
+
});
|
|
1359
1326
|
}
|
|
1360
1327
|
const UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE = 'UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME';
|
|
1361
1328
|
function unknownScheduledFunctionDevelopmentFunctionName(name) {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1329
|
+
return badRequestError({
|
|
1330
|
+
code: UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE,
|
|
1331
|
+
message: `Unknown function with name "${name}"`,
|
|
1332
|
+
data: {
|
|
1333
|
+
name
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1369
1336
|
}
|
|
1370
1337
|
const UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE = 'UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE';
|
|
1371
1338
|
function unknownScheduledFunctionDevelopmentFunctionType(type) {
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1339
|
+
return badRequestError({
|
|
1340
|
+
code: UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE,
|
|
1341
|
+
message: `Unknown type "${type}"`,
|
|
1342
|
+
data: {
|
|
1343
|
+
type
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1379
1346
|
}
|
|
1380
1347
|
|
|
1381
1348
|
function makeScheduledFunctionDevelopmentFunction(config) {
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1349
|
+
const { allScheduledFunctions } = config;
|
|
1350
|
+
const getListValues = util.cachedGetter(() => {
|
|
1351
|
+
const result = [];
|
|
1352
|
+
util.forEachKeyValue(allScheduledFunctions, {
|
|
1353
|
+
forEach: (x) => {
|
|
1354
|
+
const [functionName, config] = x;
|
|
1355
|
+
result.push({
|
|
1356
|
+
name: functionName.toString()
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1392
1359
|
});
|
|
1393
|
-
|
|
1360
|
+
return result;
|
|
1394
1361
|
});
|
|
1395
|
-
return
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1362
|
+
return async (request) => {
|
|
1363
|
+
const { data } = request;
|
|
1364
|
+
const { type } = data;
|
|
1365
|
+
switch (type) {
|
|
1366
|
+
case 'run':
|
|
1367
|
+
const targetRunName = data.run;
|
|
1368
|
+
if (!targetRunName) {
|
|
1369
|
+
throw noRunNameSpecifiedForScheduledFunctionDevelopmentFunction();
|
|
1370
|
+
}
|
|
1371
|
+
const targetFunction = allScheduledFunctions[targetRunName];
|
|
1372
|
+
if (!targetFunction) {
|
|
1373
|
+
throw unknownScheduledFunctionDevelopmentFunctionName(targetRunName);
|
|
1374
|
+
}
|
|
1375
|
+
try {
|
|
1376
|
+
await targetFunction._runNow();
|
|
1377
|
+
}
|
|
1378
|
+
catch (e) {
|
|
1379
|
+
console.error(`Failed manually running task "${targetRunName}".`, e);
|
|
1380
|
+
throw e;
|
|
1381
|
+
}
|
|
1382
|
+
return {
|
|
1383
|
+
type: 'run',
|
|
1384
|
+
success: true
|
|
1385
|
+
};
|
|
1386
|
+
case 'list':
|
|
1387
|
+
return {
|
|
1388
|
+
type: 'list',
|
|
1389
|
+
list: getListValues()
|
|
1390
|
+
};
|
|
1391
|
+
default:
|
|
1392
|
+
throw unknownScheduledFunctionDevelopmentFunctionType(type);
|
|
1419
1393
|
}
|
|
1420
|
-
|
|
1421
|
-
type: 'run',
|
|
1422
|
-
success: true
|
|
1423
|
-
};
|
|
1424
|
-
case 'list':
|
|
1425
|
-
return {
|
|
1426
|
-
type: 'list',
|
|
1427
|
-
list: getListValues()
|
|
1428
|
-
};
|
|
1429
|
-
default:
|
|
1430
|
-
throw unknownScheduledFunctionDevelopmentFunctionType(type);
|
|
1431
|
-
}
|
|
1432
|
-
};
|
|
1394
|
+
};
|
|
1433
1395
|
}
|
|
1434
1396
|
|
|
1435
1397
|
function setNestContextOnRequest(makeNestContext, request) {
|
|
1436
|
-
|
|
1437
|
-
|
|
1398
|
+
request.nest = makeNestContext(request.nestApplication);
|
|
1399
|
+
return request;
|
|
1438
1400
|
}
|
|
1439
1401
|
/**
|
|
1440
1402
|
* Wraps the input OnCallWithNestContext function to flag it as optional to have auth data.
|
|
@@ -1443,9 +1405,9 @@ function setNestContextOnRequest(makeNestContext, request) {
|
|
|
1443
1405
|
* @returns
|
|
1444
1406
|
*/
|
|
1445
1407
|
function optionalAuthContext(fn) {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1408
|
+
const fnWithOptionalAuth = ((request) => fn(request));
|
|
1409
|
+
fnWithOptionalAuth._requireAuth = false;
|
|
1410
|
+
return fnWithOptionalAuth;
|
|
1449
1411
|
}
|
|
1450
1412
|
/**
|
|
1451
1413
|
* Asserts that the input request has auth data if the inputOnCallWithAuthAwareNestRequireAuthRef object is flagged to require auth.
|
|
@@ -1454,9 +1416,9 @@ function optionalAuthContext(fn) {
|
|
|
1454
1416
|
* @param request
|
|
1455
1417
|
*/
|
|
1456
1418
|
function assertRequestRequiresAuthForFunction(fn, request) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1419
|
+
if (fn._requireAuth !== false) {
|
|
1420
|
+
assertIsContextWithAuthData(request);
|
|
1421
|
+
}
|
|
1460
1422
|
}
|
|
1461
1423
|
/**
|
|
1462
1424
|
* Creates an OnCallWithNestContext wrapper that validates the input CallableContext to assert the context has auth data before entering the function.
|
|
@@ -1465,64 +1427,59 @@ function assertRequestRequiresAuthForFunction(fn, request) {
|
|
|
1465
1427
|
* @returns
|
|
1466
1428
|
*/
|
|
1467
1429
|
function inAuthContext(fn) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1430
|
+
return (request) => {
|
|
1431
|
+
assertIsContextWithAuthData(request);
|
|
1432
|
+
return fn(request);
|
|
1433
|
+
};
|
|
1472
1434
|
}
|
|
1473
1435
|
|
|
1474
1436
|
function firebaseServerDevFunctions(config) {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1437
|
+
const { enabled, secure, nest, developerFunctionsMap, onCallFactory, allScheduledFunctions, disableDevelopmentScheduleFunction } = config;
|
|
1438
|
+
let dev;
|
|
1439
|
+
if (enabled) {
|
|
1440
|
+
const fullFunctionsMap = {
|
|
1441
|
+
...developerFunctionsMap
|
|
1442
|
+
};
|
|
1443
|
+
if (allScheduledFunctions && disableDevelopmentScheduleFunction !== false) {
|
|
1444
|
+
fullFunctionsMap[firebase.SCHEDULED_FUNCTION_DEV_FUNCTION_SPECIFIER] = makeScheduledFunctionDevelopmentFunction({
|
|
1445
|
+
allScheduledFunctions
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
let onCallFunction = onCallDevelopmentFunction(fullFunctionsMap);
|
|
1449
|
+
if (secure != false) {
|
|
1450
|
+
onCallFunction = inAuthContext(onCallFunction);
|
|
1451
|
+
}
|
|
1452
|
+
dev = onCallFactory(onCallFunction)(nest);
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
dev = onCallFactory(async (x) => {
|
|
1456
|
+
throw unavailableError({
|
|
1457
|
+
message: 'developer tools service is not enabled.'
|
|
1458
|
+
});
|
|
1459
|
+
})(nest);
|
|
1460
|
+
}
|
|
1461
|
+
return {
|
|
1462
|
+
dev
|
|
1488
1463
|
};
|
|
1489
|
-
if (allScheduledFunctions && disableDevelopmentScheduleFunction !== false) {
|
|
1490
|
-
fullFunctionsMap[firebase.SCHEDULED_FUNCTION_DEV_FUNCTION_SPECIFIER] = makeScheduledFunctionDevelopmentFunction({
|
|
1491
|
-
allScheduledFunctions
|
|
1492
|
-
});
|
|
1493
|
-
}
|
|
1494
|
-
let onCallFunction = onCallDevelopmentFunction(fullFunctionsMap);
|
|
1495
|
-
if (secure != false) {
|
|
1496
|
-
onCallFunction = inAuthContext(onCallFunction);
|
|
1497
|
-
}
|
|
1498
|
-
dev = onCallFactory(onCallFunction)(nest);
|
|
1499
|
-
} else {
|
|
1500
|
-
dev = onCallFactory(async x => {
|
|
1501
|
-
throw unavailableError({
|
|
1502
|
-
message: 'developer tools service is not enabled.'
|
|
1503
|
-
});
|
|
1504
|
-
})(nest);
|
|
1505
|
-
}
|
|
1506
|
-
return {
|
|
1507
|
-
dev
|
|
1508
|
-
};
|
|
1509
1464
|
}
|
|
1510
1465
|
|
|
1511
1466
|
exports.DefaultFirebaseServerEnvService = class DefaultFirebaseServerEnvService extends nestjs.ServerEnvironmentService {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1467
|
+
/**
|
|
1468
|
+
* Enabled when not in production and not in a testing environment.
|
|
1469
|
+
*/
|
|
1470
|
+
get developmentSchedulerEnabled() {
|
|
1471
|
+
return !this.isProduction && !this.isTestingEnv;
|
|
1472
|
+
}
|
|
1518
1473
|
};
|
|
1519
|
-
exports.DefaultFirebaseServerEnvService = __decorate([
|
|
1474
|
+
exports.DefaultFirebaseServerEnvService = __decorate([
|
|
1475
|
+
common.Injectable()
|
|
1476
|
+
], exports.DefaultFirebaseServerEnvService);
|
|
1520
1477
|
|
|
1521
1478
|
function nestAppIsProductionEnvironment(nest) {
|
|
1522
|
-
|
|
1479
|
+
return () => nest().then((x) => x.get(FirebaseServerEnvService).isProduction);
|
|
1523
1480
|
}
|
|
1524
1481
|
function nestAppHasDevelopmentSchedulerEnabled(nest) {
|
|
1525
|
-
|
|
1482
|
+
return () => nest().then((x) => x.get(FirebaseServerEnvService).developmentSchedulerEnabled);
|
|
1526
1483
|
}
|
|
1527
1484
|
|
|
1528
1485
|
// MARK: Tokens
|
|
@@ -1537,28 +1494,38 @@ const FIREBASE_FIRESTORE_CONTEXT_TOKEN = 'FIREBASE_FIRESTORE_CONTEXT_TOKEN';
|
|
|
1537
1494
|
/**
|
|
1538
1495
|
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
1539
1496
|
*/
|
|
1540
|
-
exports.FirebaseServerFirestoreModule = class FirebaseServerFirestoreModule {
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1497
|
+
exports.FirebaseServerFirestoreModule = class FirebaseServerFirestoreModule {
|
|
1498
|
+
};
|
|
1499
|
+
exports.FirebaseServerFirestoreModule = __decorate([
|
|
1500
|
+
common.Module({
|
|
1501
|
+
providers: [
|
|
1502
|
+
{
|
|
1503
|
+
provide: FIREBASE_FIRESTORE_TOKEN,
|
|
1504
|
+
useFactory: (app) => app.firestore(),
|
|
1505
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
1506
|
+
}
|
|
1507
|
+
],
|
|
1508
|
+
exports: [FIREBASE_FIRESTORE_TOKEN]
|
|
1509
|
+
})
|
|
1510
|
+
], exports.FirebaseServerFirestoreModule);
|
|
1549
1511
|
/**
|
|
1550
1512
|
* Nest provider module for firebase that includes the FirebaseServerFirestoreModule and provides a value for FIRESTORE_CONTEXT_TOKEN using the googleCloudFirestoreContextFactory.
|
|
1551
1513
|
*/
|
|
1552
|
-
exports.FirebaseServerFirestoreContextModule = class FirebaseServerFirestoreContextModule {
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1514
|
+
exports.FirebaseServerFirestoreContextModule = class FirebaseServerFirestoreContextModule {
|
|
1515
|
+
};
|
|
1516
|
+
exports.FirebaseServerFirestoreContextModule = __decorate([
|
|
1517
|
+
common.Module({
|
|
1518
|
+
imports: [exports.FirebaseServerFirestoreModule],
|
|
1519
|
+
providers: [
|
|
1520
|
+
{
|
|
1521
|
+
provide: FIREBASE_FIRESTORE_CONTEXT_TOKEN,
|
|
1522
|
+
useFactory: googleCloudFirestoreContextFactory,
|
|
1523
|
+
inject: [FIREBASE_FIRESTORE_TOKEN]
|
|
1524
|
+
}
|
|
1525
|
+
],
|
|
1526
|
+
exports: [exports.FirebaseServerFirestoreModule, FIREBASE_FIRESTORE_CONTEXT_TOKEN]
|
|
1527
|
+
})
|
|
1528
|
+
], exports.FirebaseServerFirestoreContextModule);
|
|
1562
1529
|
/**
|
|
1563
1530
|
* Used to configure a Nestjs provider for a FirestoreCollections-type object that is initialized with a FirestoreContext.
|
|
1564
1531
|
*
|
|
@@ -1566,15 +1533,14 @@ exports.FirebaseServerFirestoreContextModule = __decorate([common.Module({
|
|
|
1566
1533
|
* @param useFactory
|
|
1567
1534
|
* @returns
|
|
1568
1535
|
*/
|
|
1569
|
-
function provideAppFirestoreCollections({
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
}];
|
|
1536
|
+
function provideAppFirestoreCollections({ provide, useFactory }) {
|
|
1537
|
+
return [
|
|
1538
|
+
{
|
|
1539
|
+
provide,
|
|
1540
|
+
useFactory,
|
|
1541
|
+
inject: [FIREBASE_FIRESTORE_CONTEXT_TOKEN]
|
|
1542
|
+
}
|
|
1543
|
+
];
|
|
1578
1544
|
}
|
|
1579
1545
|
/**
|
|
1580
1546
|
* Convenience function used to generate ModuleMetadata for an app's Firestore related modules and an appFirestoreCollection
|
|
@@ -1584,11 +1550,11 @@ function provideAppFirestoreCollections({
|
|
|
1584
1550
|
* @returns
|
|
1585
1551
|
*/
|
|
1586
1552
|
function appFirestoreModuleMetadata(config) {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1553
|
+
return {
|
|
1554
|
+
imports: [exports.FirebaseServerFirestoreContextModule, ...(config.imports ?? [])],
|
|
1555
|
+
exports: [exports.FirebaseServerFirestoreContextModule, config.provide, ...(config.exports ?? [])],
|
|
1556
|
+
providers: [...provideAppFirestoreCollections(config), ...(config.providers ?? [])]
|
|
1557
|
+
};
|
|
1592
1558
|
}
|
|
1593
1559
|
|
|
1594
1560
|
/**
|
|
@@ -1599,9 +1565,9 @@ function appFirestoreModuleMetadata(config) {
|
|
|
1599
1565
|
* @returns
|
|
1600
1566
|
*/
|
|
1601
1567
|
function makeBlockingFunctionWithHandler(blockingFunctionBuilder, handler, opts) {
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1568
|
+
const blockingFn = opts != null ? blockingFunctionBuilder(opts, handler) : blockingFunctionBuilder(handler);
|
|
1569
|
+
blockingFn.__handler = handler;
|
|
1570
|
+
return blockingFn;
|
|
1605
1571
|
}
|
|
1606
1572
|
/**
|
|
1607
1573
|
* Creates a BlockingFunctionHandlerWithNestContextFactory.
|
|
@@ -1611,18 +1577,18 @@ function makeBlockingFunctionWithHandler(blockingFunctionBuilder, handler, opts)
|
|
|
1611
1577
|
* @returns
|
|
1612
1578
|
*/
|
|
1613
1579
|
function blockingFunctionHandlerWithNestContextFactory(makeNestContext) {
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1580
|
+
return (fn) => {
|
|
1581
|
+
return (nestAppPromiseGetter) => {
|
|
1582
|
+
const handlerBuilder = (handler) => {
|
|
1583
|
+
const fnHandler = (event) => nestAppPromiseGetter().then((nestApplication) => handler({
|
|
1584
|
+
...event,
|
|
1585
|
+
nest: makeNestContext(nestApplication)
|
|
1586
|
+
}));
|
|
1587
|
+
return fnHandler;
|
|
1588
|
+
};
|
|
1589
|
+
return fn(handlerBuilder);
|
|
1590
|
+
};
|
|
1624
1591
|
};
|
|
1625
|
-
};
|
|
1626
1592
|
}
|
|
1627
1593
|
|
|
1628
1594
|
/**
|
|
@@ -1632,15 +1598,12 @@ function blockingFunctionHandlerWithNestContextFactory(makeNestContext) {
|
|
|
1632
1598
|
* @returns
|
|
1633
1599
|
*/
|
|
1634
1600
|
function onCallHandlerWithNestApplicationFactory(defaultOpts = {}) {
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
nestApplication
|
|
1642
|
-
})));
|
|
1643
|
-
};
|
|
1601
|
+
return (fn, opts) => {
|
|
1602
|
+
return (nestAppPromiseGetter) => v2.https.onCall({ ...defaultOpts, ...opts }, (request) => nestAppPromiseGetter().then((nestApplication) => fn({
|
|
1603
|
+
...request,
|
|
1604
|
+
nestApplication
|
|
1605
|
+
})));
|
|
1606
|
+
};
|
|
1644
1607
|
}
|
|
1645
1608
|
/**
|
|
1646
1609
|
* Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function.
|
|
@@ -1650,7 +1613,7 @@ function onCallHandlerWithNestApplicationFactory(defaultOpts = {}) {
|
|
|
1650
1613
|
* @returns
|
|
1651
1614
|
*/
|
|
1652
1615
|
function onCallHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
1653
|
-
|
|
1616
|
+
return (fn, opts) => appFactory((request) => fn(setNestContextOnRequest(makeNestContext, request)), opts);
|
|
1654
1617
|
}
|
|
1655
1618
|
|
|
1656
1619
|
/**
|
|
@@ -1661,30 +1624,30 @@ function onCallHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
|
1661
1624
|
* @returns
|
|
1662
1625
|
*/
|
|
1663
1626
|
function cloudEventHandlerWithNestContextFactory(makeNestContext) {
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1627
|
+
return (fn) => {
|
|
1628
|
+
return (nestAppPromiseGetter) => {
|
|
1629
|
+
const handlerBuilder = (handler) => {
|
|
1630
|
+
const fnHandler = (event) => nestAppPromiseGetter().then((nestApplication) => handler({
|
|
1631
|
+
...event,
|
|
1632
|
+
nest: makeNestContext(nestApplication)
|
|
1633
|
+
}));
|
|
1634
|
+
return fnHandler;
|
|
1635
|
+
};
|
|
1636
|
+
return fn(handlerBuilder);
|
|
1637
|
+
};
|
|
1674
1638
|
};
|
|
1675
|
-
};
|
|
1676
1639
|
}
|
|
1677
1640
|
|
|
1678
1641
|
function setNestContextOnScheduleRequest(makeNestContext, request) {
|
|
1679
|
-
|
|
1680
|
-
|
|
1642
|
+
request.nest = makeNestContext(request.nestApplication);
|
|
1643
|
+
return request;
|
|
1681
1644
|
}
|
|
1682
1645
|
|
|
1683
1646
|
function makeOnScheduleHandlerWithNestApplicationRequest(nestApplication, scheduleContext) {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1647
|
+
return {
|
|
1648
|
+
nestApplication,
|
|
1649
|
+
scheduleContext
|
|
1650
|
+
};
|
|
1688
1651
|
}
|
|
1689
1652
|
/**
|
|
1690
1653
|
* Creates a factory for generating OnCallWithNestApplication functions.
|
|
@@ -1693,27 +1656,29 @@ function makeOnScheduleHandlerWithNestApplicationRequest(nestApplication, schedu
|
|
|
1693
1656
|
* @returns
|
|
1694
1657
|
*/
|
|
1695
1658
|
function onScheduleHandlerWithNestApplicationFactory(baseScheduleConfig) {
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1659
|
+
return (inputSchedule, fn) => {
|
|
1660
|
+
const schedule = util.mergeObjects([baseScheduleConfig, inputSchedule]);
|
|
1661
|
+
if (!schedule.schedule) {
|
|
1662
|
+
if (schedule.cron) {
|
|
1663
|
+
if (typeof schedule.cron === 'number') {
|
|
1664
|
+
schedule.schedule = util.cronExpressionRepeatingEveryNMinutes(schedule.cron);
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
schedule.schedule = schedule.cron;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
else {
|
|
1671
|
+
throw new Error('Missing required "cron" or "schedule" variable for configuration.');
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
return (nestAppPromiseGetter) => {
|
|
1675
|
+
const runNow = (scheduleContext) => nestAppPromiseGetter().then((x) => fn(makeOnScheduleHandlerWithNestApplicationRequest(x, scheduleContext)));
|
|
1676
|
+
const fnn = v2.scheduler.onSchedule(schedule, runNow);
|
|
1677
|
+
fnn._schedule = schedule;
|
|
1678
|
+
fnn._runNow = runNow;
|
|
1679
|
+
return fnn;
|
|
1680
|
+
};
|
|
1715
1681
|
};
|
|
1716
|
-
};
|
|
1717
1682
|
}
|
|
1718
1683
|
/**
|
|
1719
1684
|
* Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function.
|
|
@@ -1723,7 +1688,7 @@ function onScheduleHandlerWithNestApplicationFactory(baseScheduleConfig) {
|
|
|
1723
1688
|
* @returns
|
|
1724
1689
|
*/
|
|
1725
1690
|
function onScheduleHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
1726
|
-
|
|
1691
|
+
return (schedule, fn) => appFactory(schedule, (request) => fn(setNestContextOnScheduleRequest(makeNestContext, request)));
|
|
1727
1692
|
}
|
|
1728
1693
|
|
|
1729
1694
|
/**
|
|
@@ -1734,37 +1699,38 @@ function onScheduleHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
|
1734
1699
|
* @returns
|
|
1735
1700
|
*/
|
|
1736
1701
|
function taskQueueFunctionHandlerWithNestContextFactory(makeNestContext) {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1702
|
+
return (fn) => {
|
|
1703
|
+
return (nestAppPromiseGetter) => {
|
|
1704
|
+
const handlerBuilder = (handler) => {
|
|
1705
|
+
const fnHandler = (taskRequest) => nestAppPromiseGetter().then((nestApplication) => handler({
|
|
1706
|
+
...taskRequest,
|
|
1707
|
+
nest: makeNestContext(nestApplication)
|
|
1708
|
+
}));
|
|
1709
|
+
return fnHandler;
|
|
1710
|
+
};
|
|
1711
|
+
return fn(handlerBuilder);
|
|
1712
|
+
};
|
|
1747
1713
|
};
|
|
1748
|
-
};
|
|
1749
1714
|
}
|
|
1750
1715
|
// TODO(FUTURE): Add factory that also adds onTaskDispatched usage, as the above is incomplete for full usage and only sets up a function for the request.
|
|
1751
1716
|
|
|
1752
|
-
class AbstractFirebaseServerActionsContext {
|
|
1717
|
+
class AbstractFirebaseServerActionsContext {
|
|
1718
|
+
}
|
|
1753
1719
|
function firebaseServerActionsContext(options) {
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1720
|
+
return {
|
|
1721
|
+
...firebaseServerActionsTransformContext(options)
|
|
1722
|
+
};
|
|
1757
1723
|
}
|
|
1758
|
-
const defaultFirebaseServerActionsTransformFactoryLogErrorFunction = details => {
|
|
1759
|
-
|
|
1724
|
+
const defaultFirebaseServerActionsTransformFactoryLogErrorFunction = (details) => {
|
|
1725
|
+
console.log('firebaseServerActionsTransformFactory() encountered validation error: ', details);
|
|
1760
1726
|
};
|
|
1761
1727
|
function firebaseServerActionsTransformContext(options) {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1728
|
+
const firebaseServerActionTransformFactory = firebaseServerActionsTransformFactory(options);
|
|
1729
|
+
const firebaseServerActionTransformFunctionFactory = model.toTransformAndValidateFunctionResultFactory(firebaseServerActionTransformFactory);
|
|
1730
|
+
return {
|
|
1731
|
+
firebaseServerActionTransformFactory,
|
|
1732
|
+
firebaseServerActionTransformFunctionFactory
|
|
1733
|
+
};
|
|
1768
1734
|
}
|
|
1769
1735
|
const FIREBASE_SERVER_VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';
|
|
1770
1736
|
/**
|
|
@@ -1773,16 +1739,16 @@ const FIREBASE_SERVER_VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';
|
|
|
1773
1739
|
* @returns
|
|
1774
1740
|
*/
|
|
1775
1741
|
function firebaseServerValidationServerError(validationError) {
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1742
|
+
const nestValidationExceptionFactory = new common.ValidationPipe({
|
|
1743
|
+
forbidUnknownValues: false
|
|
1744
|
+
}).createExceptionFactory();
|
|
1745
|
+
const nestError = nestValidationExceptionFactory(validationError);
|
|
1746
|
+
const data = nestError.getResponse();
|
|
1747
|
+
return {
|
|
1748
|
+
message: 'One or more data/form validation errors occurred.',
|
|
1749
|
+
code: FIREBASE_SERVER_VALIDATION_ERROR_CODE,
|
|
1750
|
+
data
|
|
1751
|
+
};
|
|
1786
1752
|
}
|
|
1787
1753
|
/**
|
|
1788
1754
|
* Creates a new badRequestError with the validation error details as the response data.
|
|
@@ -1791,48 +1757,41 @@ function firebaseServerValidationServerError(validationError) {
|
|
|
1791
1757
|
* @returns
|
|
1792
1758
|
*/
|
|
1793
1759
|
function firebaseServerValidationError(validationError) {
|
|
1794
|
-
|
|
1795
|
-
|
|
1760
|
+
const serverError = firebaseServerValidationServerError(validationError);
|
|
1761
|
+
return badRequestError(serverError);
|
|
1796
1762
|
}
|
|
1797
1763
|
function firebaseServerActionsTransformFactory(options) {
|
|
1798
|
-
|
|
1799
|
-
logError
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
logErrorFunction(data);
|
|
1810
|
-
throw badRequestError(serverError);
|
|
1811
|
-
},
|
|
1812
|
-
defaultValidationOptions
|
|
1813
|
-
});
|
|
1764
|
+
const { logError, defaultValidationOptions } = options ?? {};
|
|
1765
|
+
const logErrorFunction = logError !== false ? (typeof logError === 'function' ? logError : defaultFirebaseServerActionsTransformFactoryLogErrorFunction) : util.mapIdentityFunction;
|
|
1766
|
+
return model.transformAndValidateObjectFactory({
|
|
1767
|
+
handleValidationError: (validationError) => {
|
|
1768
|
+
const serverError = firebaseServerValidationServerError(validationError);
|
|
1769
|
+
const { data } = serverError;
|
|
1770
|
+
logErrorFunction(data);
|
|
1771
|
+
throw badRequestError(serverError);
|
|
1772
|
+
},
|
|
1773
|
+
defaultValidationOptions
|
|
1774
|
+
});
|
|
1814
1775
|
}
|
|
1815
1776
|
|
|
1816
1777
|
function injectNestIntoRequest(nest, request) {
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1778
|
+
return {
|
|
1779
|
+
...request,
|
|
1780
|
+
nest
|
|
1781
|
+
};
|
|
1821
1782
|
}
|
|
1822
1783
|
function injectNestApplicationContextIntoRequest(nestContext, request) {
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1784
|
+
return {
|
|
1785
|
+
...request,
|
|
1786
|
+
nestApplication: nestContext
|
|
1787
|
+
};
|
|
1827
1788
|
}
|
|
1828
1789
|
|
|
1829
1790
|
/**
|
|
1830
1791
|
* Can be injected to retrieve information about the global prefix configured for the app.
|
|
1831
1792
|
*/
|
|
1832
1793
|
class GlobalRoutePrefixConfig {
|
|
1833
|
-
|
|
1834
|
-
this.globalApiRoutePrefix = void 0;
|
|
1835
|
-
}
|
|
1794
|
+
globalApiRoutePrefix;
|
|
1836
1795
|
}
|
|
1837
1796
|
|
|
1838
1797
|
/**
|
|
@@ -1841,33 +1800,38 @@ class GlobalRoutePrefixConfig {
|
|
|
1841
1800
|
* It ignores all webhook paths by default.
|
|
1842
1801
|
*/
|
|
1843
1802
|
exports.FirebaseAppCheckMiddleware = class FirebaseAppCheckMiddleware {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1803
|
+
globalRoutePrefixConfig;
|
|
1804
|
+
logger = new common.Logger('FirebaseAppCheckMiddleware');
|
|
1805
|
+
_ignoredWebhookPath;
|
|
1806
|
+
constructor(globalRoutePrefixConfig) {
|
|
1807
|
+
this.globalRoutePrefixConfig = globalRoutePrefixConfig;
|
|
1808
|
+
this._ignoredWebhookPath = this.globalRoutePrefixConfig?.globalApiRoutePrefix ? `${this.globalRoutePrefixConfig.globalApiRoutePrefix}${nestjs.DEFAULT_BASE_WEBHOOK_PATH}` : nestjs.DEFAULT_BASE_WEBHOOK_PATH;
|
|
1809
|
+
}
|
|
1810
|
+
async use(req, res, next) {
|
|
1811
|
+
const isIgnoredRoute = this.isIgnoredRequest(req);
|
|
1812
|
+
let error;
|
|
1813
|
+
if (!isIgnoredRoute) {
|
|
1814
|
+
error = await verifyAppCheckInRequest(req);
|
|
1815
|
+
if (error) {
|
|
1816
|
+
this.logger.error('app check token failed verify');
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
next(error);
|
|
1820
|
+
}
|
|
1821
|
+
isIgnoredRequest(req) {
|
|
1822
|
+
const isIgnoredRoute = req.skipAppCheck || this.isIgnoredPath(req.baseUrl);
|
|
1823
|
+
return isIgnoredRoute;
|
|
1824
|
+
}
|
|
1825
|
+
isIgnoredPath(path) {
|
|
1826
|
+
return path.startsWith(this._ignoredWebhookPath);
|
|
1827
|
+
}
|
|
1869
1828
|
};
|
|
1870
|
-
exports.FirebaseAppCheckMiddleware = __decorate([
|
|
1829
|
+
exports.FirebaseAppCheckMiddleware = __decorate([
|
|
1830
|
+
common.Injectable(),
|
|
1831
|
+
__param(0, common.Optional()),
|
|
1832
|
+
__param(0, common.Inject(GlobalRoutePrefixConfig)),
|
|
1833
|
+
__metadata("design:paramtypes", [Object])
|
|
1834
|
+
], exports.FirebaseAppCheckMiddleware);
|
|
1871
1835
|
/**
|
|
1872
1836
|
* Verifies the AppCheck parameter. If it fails, a value is returned.
|
|
1873
1837
|
*
|
|
@@ -1877,50 +1841,54 @@ exports.FirebaseAppCheckMiddleware = __decorate([common.Injectable(), __param(0,
|
|
|
1877
1841
|
* @returns
|
|
1878
1842
|
*/
|
|
1879
1843
|
async function verifyAppCheckInRequest(req) {
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
} else {
|
|
1885
|
-
// verify the token
|
|
1886
|
-
try {
|
|
1887
|
-
await admin.appCheck().verifyToken(appCheckToken);
|
|
1888
|
-
} catch (e) {
|
|
1889
|
-
error = new common.ForbiddenException();
|
|
1844
|
+
const appCheckToken = req.header('X-Firebase-AppCheck');
|
|
1845
|
+
let error;
|
|
1846
|
+
if (!appCheckToken) {
|
|
1847
|
+
error = new common.ForbiddenException();
|
|
1890
1848
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1849
|
+
else {
|
|
1850
|
+
// verify the token
|
|
1851
|
+
try {
|
|
1852
|
+
await admin.appCheck().verifyToken(appCheckToken);
|
|
1853
|
+
}
|
|
1854
|
+
catch (e) {
|
|
1855
|
+
error = new common.ForbiddenException();
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
return error;
|
|
1893
1859
|
}
|
|
1894
1860
|
|
|
1895
1861
|
/**
|
|
1896
1862
|
* Convenience class that mirrors the ConfigureAppCheckMiddlewareModule class in @dereekb/nestjs, but for Firebase apps.
|
|
1897
1863
|
*/
|
|
1898
1864
|
exports.ConfigureFirebaseAppCheckMiddlewareModule = class ConfigureFirebaseAppCheckMiddlewareModule {
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1905
|
-
}
|
|
1865
|
+
logger = new common.Logger('ConfigureFirebaseAppCheckMiddlewareModule');
|
|
1866
|
+
configure(consumer) {
|
|
1867
|
+
consumer.apply(exports.FirebaseAppCheckMiddleware).forRoutes('*');
|
|
1868
|
+
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1869
|
+
}
|
|
1906
1870
|
};
|
|
1907
|
-
exports.ConfigureFirebaseAppCheckMiddlewareModule = __decorate([
|
|
1871
|
+
exports.ConfigureFirebaseAppCheckMiddlewareModule = __decorate([
|
|
1872
|
+
common.Module({})
|
|
1873
|
+
], exports.ConfigureFirebaseAppCheckMiddlewareModule);
|
|
1908
1874
|
|
|
1909
1875
|
/**
|
|
1910
1876
|
* nestjs decorator that will instruct FirebaseAppCheckMiddleware to skip AppCheck for related requests.
|
|
1911
1877
|
*/
|
|
1912
1878
|
const SkipAppCheck = common.createParamDecorator(async (_, context) => {
|
|
1913
|
-
|
|
1914
|
-
|
|
1879
|
+
const req = context.switchToHttp().getRequest();
|
|
1880
|
+
req.skipAppCheck = true;
|
|
1915
1881
|
});
|
|
1916
1882
|
|
|
1917
1883
|
exports.FirebaseRawBodyMiddleware = class FirebaseRawBodyMiddleware {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1884
|
+
use(req, res, next) {
|
|
1885
|
+
req.body = req.rawBody;
|
|
1886
|
+
next();
|
|
1887
|
+
}
|
|
1922
1888
|
};
|
|
1923
|
-
exports.FirebaseRawBodyMiddleware = __decorate([
|
|
1889
|
+
exports.FirebaseRawBodyMiddleware = __decorate([
|
|
1890
|
+
common.Injectable()
|
|
1891
|
+
], exports.FirebaseRawBodyMiddleware);
|
|
1924
1892
|
|
|
1925
1893
|
/**
|
|
1926
1894
|
* Convenience class that mirrors the ConfigureWebhookMiddlewareModule class in @dereekb/nestjs, but for Firebase apps.
|
|
@@ -1928,60 +1896,59 @@ exports.FirebaseRawBodyMiddleware = __decorate([common.Injectable()], exports.Fi
|
|
|
1928
1896
|
* Requests to /webhook/* have their request.body value set to the rawBody.
|
|
1929
1897
|
*/
|
|
1930
1898
|
exports.ConfigureFirebaseWebhookMiddlewareModule = class ConfigureFirebaseWebhookMiddlewareModule {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1937
|
-
}
|
|
1899
|
+
logger = new common.Logger('ConfigureFirebaseWebhookMiddlewareModule');
|
|
1900
|
+
configure(consumer) {
|
|
1901
|
+
consumer.apply(exports.FirebaseRawBodyMiddleware).forRoutes(nestjs.DEFAULT_WEBHOOK_MIDDLEWARE_ROUTE_INFO);
|
|
1902
|
+
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1903
|
+
}
|
|
1938
1904
|
};
|
|
1939
|
-
exports.ConfigureFirebaseWebhookMiddlewareModule = __decorate([
|
|
1905
|
+
exports.ConfigureFirebaseWebhookMiddlewareModule = __decorate([
|
|
1906
|
+
common.Module({})
|
|
1907
|
+
], exports.ConfigureFirebaseWebhookMiddlewareModule);
|
|
1940
1908
|
|
|
1941
|
-
const nestFirebaseDoesNotExistError = firebaseContextGrantedModelRoles => {
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1909
|
+
const nestFirebaseDoesNotExistError = (firebaseContextGrantedModelRoles) => {
|
|
1910
|
+
return modelNotAvailableError({
|
|
1911
|
+
data: {
|
|
1912
|
+
id: firebaseContextGrantedModelRoles.data?.document.key,
|
|
1913
|
+
type: firebaseContextGrantedModelRoles.data?.document.modelType
|
|
1914
|
+
}
|
|
1915
|
+
});
|
|
1948
1916
|
};
|
|
1949
1917
|
const nestFirebaseForbiddenPermissionError = (firebaseContextGrantedModelRoles, roles) => {
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1918
|
+
return forbiddenError({
|
|
1919
|
+
data: {
|
|
1920
|
+
id: firebaseContextGrantedModelRoles.data?.document.key,
|
|
1921
|
+
type: firebaseContextGrantedModelRoles.data?.document.modelType,
|
|
1922
|
+
roles
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1957
1925
|
};
|
|
1958
1926
|
|
|
1959
1927
|
function onCallSpecifierHandler(config) {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
return fn;
|
|
1928
|
+
const map = util.objectToMap(config);
|
|
1929
|
+
const fn = (request) => {
|
|
1930
|
+
const { specifier = firebase.MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT } = request;
|
|
1931
|
+
const handler = map.get(specifier);
|
|
1932
|
+
if (handler != null) {
|
|
1933
|
+
assertRequestRequiresAuthForFunction(handler, request);
|
|
1934
|
+
return handler(request);
|
|
1935
|
+
}
|
|
1936
|
+
else {
|
|
1937
|
+
throw unknownModelCrudFunctionSpecifierError(specifier);
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
fn._requireAuth = false;
|
|
1941
|
+
return fn;
|
|
1975
1942
|
}
|
|
1976
1943
|
function unknownModelCrudFunctionSpecifierError(specifier) {
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1944
|
+
return badRequestError(util.serverError({
|
|
1945
|
+
status: 400,
|
|
1946
|
+
code: 'UNKNOWN_SPECIFIER_ERROR',
|
|
1947
|
+
message: 'Invalid/unknown specifier for this function.',
|
|
1948
|
+
data: {
|
|
1949
|
+
specifier
|
|
1950
|
+
}
|
|
1951
|
+
}));
|
|
1985
1952
|
}
|
|
1986
1953
|
|
|
1987
1954
|
/**
|
|
@@ -1991,497 +1958,455 @@ function unknownModelCrudFunctionSpecifierError(specifier) {
|
|
|
1991
1958
|
* @returns
|
|
1992
1959
|
*/
|
|
1993
1960
|
function onCallModel(map, config = {}) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
return callFn(request);
|
|
2013
|
-
} else {
|
|
2014
|
-
throw onCallModelUnknownCallTypeError(call);
|
|
2015
|
-
}
|
|
2016
|
-
} else {
|
|
2017
|
-
throw onCallModelMissingCallTypeError();
|
|
2018
|
-
}
|
|
2019
|
-
};
|
|
1961
|
+
const { preAssert = () => undefined } = config;
|
|
1962
|
+
return (request) => {
|
|
1963
|
+
const call = request.data?.call;
|
|
1964
|
+
if (call) {
|
|
1965
|
+
const callFn = map[call];
|
|
1966
|
+
if (callFn) {
|
|
1967
|
+
const { specifier, modelType } = request.data;
|
|
1968
|
+
preAssert({ call, request, modelType, specifier });
|
|
1969
|
+
return callFn(request);
|
|
1970
|
+
}
|
|
1971
|
+
else {
|
|
1972
|
+
throw onCallModelUnknownCallTypeError(call);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
else {
|
|
1976
|
+
throw onCallModelMissingCallTypeError();
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
2020
1979
|
}
|
|
2021
1980
|
function onCallModelMissingCallTypeError() {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
1981
|
+
return badRequestError(util.serverError({
|
|
1982
|
+
status: 400,
|
|
1983
|
+
code: 'CALL_TYPE_MISSING_ERROR',
|
|
1984
|
+
message: `The call type was missing from the request.`
|
|
1985
|
+
}));
|
|
2027
1986
|
}
|
|
2028
1987
|
function onCallModelUnknownCallTypeError(call) {
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1988
|
+
return badRequestError(util.serverError({
|
|
1989
|
+
status: 400,
|
|
1990
|
+
code: 'UNKNOWN_CALL_TYPE_ERROR',
|
|
1991
|
+
message: `Unknown call type "${call}".`,
|
|
1992
|
+
data: {
|
|
1993
|
+
call
|
|
1994
|
+
}
|
|
1995
|
+
}));
|
|
2037
1996
|
}
|
|
2038
1997
|
function _onCallWithCallTypeFunction(map, config) {
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
return crudFn({
|
|
2058
|
-
...request,
|
|
2059
|
-
specifier,
|
|
2060
|
-
data: request.data.data
|
|
2061
|
-
});
|
|
2062
|
-
} else {
|
|
2063
|
-
throw throwOnUnknownModelType(modelType);
|
|
2064
|
-
}
|
|
2065
|
-
};
|
|
1998
|
+
const { callType, crudType, preAssert = () => undefined, throwOnUnknownModelType } = config;
|
|
1999
|
+
return (request) => {
|
|
2000
|
+
const modelType = request.data?.modelType;
|
|
2001
|
+
const crudFn = map[modelType];
|
|
2002
|
+
if (crudFn) {
|
|
2003
|
+
const specifier = request.data.specifier;
|
|
2004
|
+
assertRequestRequiresAuthForFunction(crudFn, request);
|
|
2005
|
+
preAssert({ call: callType, request, modelType, specifier });
|
|
2006
|
+
return crudFn({
|
|
2007
|
+
...request,
|
|
2008
|
+
specifier,
|
|
2009
|
+
data: request.data.data
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
else {
|
|
2013
|
+
throw throwOnUnknownModelType(modelType);
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2066
2016
|
}
|
|
2067
2017
|
|
|
2068
2018
|
function onCallCreateModel(map, config = {}) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
throwOnUnknownModelType: createModelUnknownModelTypeError
|
|
2077
|
-
});
|
|
2019
|
+
const { preAssert } = config;
|
|
2020
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2021
|
+
callType: 'create',
|
|
2022
|
+
crudType: 'create',
|
|
2023
|
+
preAssert,
|
|
2024
|
+
throwOnUnknownModelType: createModelUnknownModelTypeError
|
|
2025
|
+
});
|
|
2078
2026
|
}
|
|
2079
2027
|
function createModelUnknownModelTypeError(modelType) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2028
|
+
return badRequestError(util.serverError({
|
|
2029
|
+
status: 400,
|
|
2030
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2031
|
+
message: `Invalid type "${modelType}" to create.`,
|
|
2032
|
+
data: {
|
|
2033
|
+
modelType
|
|
2034
|
+
}
|
|
2035
|
+
}));
|
|
2088
2036
|
}
|
|
2089
2037
|
|
|
2090
2038
|
function onCallReadModel(map, config = {}) {
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
throwOnUnknownModelType: readModelUnknownModelTypeError
|
|
2099
|
-
});
|
|
2039
|
+
const { preAssert } = config;
|
|
2040
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2041
|
+
callType: 'read',
|
|
2042
|
+
crudType: 'read',
|
|
2043
|
+
preAssert,
|
|
2044
|
+
throwOnUnknownModelType: readModelUnknownModelTypeError
|
|
2045
|
+
});
|
|
2100
2046
|
}
|
|
2101
2047
|
function readModelUnknownModelTypeError(modelType) {
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2048
|
+
return badRequestError(util.serverError({
|
|
2049
|
+
status: 400,
|
|
2050
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2051
|
+
message: 'Invalid type to read.',
|
|
2052
|
+
data: {
|
|
2053
|
+
modelType
|
|
2054
|
+
}
|
|
2055
|
+
}));
|
|
2110
2056
|
}
|
|
2111
2057
|
|
|
2112
2058
|
function onCallUpdateModel(map, config = {}) {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
throwOnUnknownModelType: updateModelUnknownModelTypeError
|
|
2121
|
-
});
|
|
2059
|
+
const { preAssert } = config;
|
|
2060
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2061
|
+
callType: 'update',
|
|
2062
|
+
crudType: 'update',
|
|
2063
|
+
preAssert,
|
|
2064
|
+
throwOnUnknownModelType: updateModelUnknownModelTypeError
|
|
2065
|
+
});
|
|
2122
2066
|
}
|
|
2123
2067
|
function updateModelUnknownModelTypeError(modelType) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2068
|
+
return badRequestError(util.serverError({
|
|
2069
|
+
status: 400,
|
|
2070
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2071
|
+
message: 'Invalid type to update.',
|
|
2072
|
+
data: {
|
|
2073
|
+
modelType
|
|
2074
|
+
}
|
|
2075
|
+
}));
|
|
2132
2076
|
}
|
|
2133
2077
|
|
|
2134
2078
|
function onCallDeleteModel(map, config = {}) {
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
throwOnUnknownModelType: deleteModelUnknownModelTypeError
|
|
2143
|
-
});
|
|
2079
|
+
const { preAssert } = config;
|
|
2080
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2081
|
+
callType: 'delete',
|
|
2082
|
+
crudType: 'delete',
|
|
2083
|
+
preAssert,
|
|
2084
|
+
throwOnUnknownModelType: deleteModelUnknownModelTypeError
|
|
2085
|
+
});
|
|
2144
2086
|
}
|
|
2145
2087
|
function deleteModelUnknownModelTypeError(modelType) {
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2088
|
+
return badRequestError(util.serverError({
|
|
2089
|
+
status: 400,
|
|
2090
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2091
|
+
message: 'Invalid type to delete.',
|
|
2092
|
+
data: {
|
|
2093
|
+
modelType
|
|
2094
|
+
}
|
|
2095
|
+
}));
|
|
2154
2096
|
}
|
|
2155
2097
|
|
|
2156
2098
|
function googleCloudStorageBucketForStorageFilePath(storage, path) {
|
|
2157
|
-
|
|
2099
|
+
return storage.bucket(path.bucketId);
|
|
2158
2100
|
}
|
|
2159
2101
|
function googleCloudStorageFileForStorageFilePath(storage, path) {
|
|
2160
|
-
|
|
2102
|
+
return googleCloudStorageBucketForStorageFilePath(storage, path).file(path.pathString);
|
|
2161
2103
|
}
|
|
2162
2104
|
function googleCloudFileMetadataToStorageMetadata(file, metadata) {
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
return {
|
|
2169
|
-
bucket: file.bucket.name,
|
|
2170
|
-
fullPath,
|
|
2171
|
-
generation,
|
|
2172
|
-
metageneration,
|
|
2173
|
-
name: file.name,
|
|
2174
|
-
size,
|
|
2175
|
-
timeCreated: metadata.timeCreated,
|
|
2176
|
-
updated: metadata.updated,
|
|
2177
|
-
md5Hash: metadata.md5Hash,
|
|
2178
|
-
cacheControl: metadata.cacheControl,
|
|
2179
|
-
contentDisposition: metadata.contentDisposition,
|
|
2180
|
-
contentEncoding: metadata.contentEncoding,
|
|
2181
|
-
contentLanguage: metadata.contentLanguage,
|
|
2182
|
-
contentType: metadata.contentType,
|
|
2183
|
-
customMetadata
|
|
2184
|
-
};
|
|
2185
|
-
}
|
|
2186
|
-
function googleCloudStorageAccessorFile(storage$1, storagePath) {
|
|
2187
|
-
const file = googleCloudStorageFileForStorageFilePath(storage$1, storagePath);
|
|
2188
|
-
function makeDownloadOptions(maxDownloadSizeBytes) {
|
|
2105
|
+
const fullPath = file.name;
|
|
2106
|
+
const generation = String(metadata.generation ?? file.generation);
|
|
2107
|
+
const metageneration = String(metadata.metageneration);
|
|
2108
|
+
const size = Number(metadata.size);
|
|
2109
|
+
const customMetadata = metadata.metadata;
|
|
2189
2110
|
return {
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2111
|
+
bucket: file.bucket.name,
|
|
2112
|
+
fullPath,
|
|
2113
|
+
generation,
|
|
2114
|
+
metageneration,
|
|
2115
|
+
name: file.name,
|
|
2116
|
+
size,
|
|
2117
|
+
timeCreated: metadata.timeCreated,
|
|
2118
|
+
updated: metadata.updated,
|
|
2119
|
+
md5Hash: metadata.md5Hash,
|
|
2120
|
+
cacheControl: metadata.cacheControl,
|
|
2121
|
+
contentDisposition: metadata.contentDisposition,
|
|
2122
|
+
contentEncoding: metadata.contentEncoding,
|
|
2123
|
+
contentLanguage: metadata.contentLanguage,
|
|
2124
|
+
contentType: metadata.contentType,
|
|
2125
|
+
customMetadata
|
|
2194
2126
|
};
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
const
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
metadata: !util.objectHasNoKeys(customMetadata) ? customMetadata : undefined
|
|
2208
|
-
});
|
|
2209
|
-
}
|
|
2210
|
-
function makeUploadOptions(options) {
|
|
2211
|
-
let metadata;
|
|
2212
|
-
if (options != null) {
|
|
2213
|
-
metadata = _configureMetadata({
|
|
2214
|
-
metadata: {
|
|
2215
|
-
...options.metadata,
|
|
2216
|
-
contentType: options.contentType ?? options.metadata?.contentType
|
|
2217
|
-
},
|
|
2218
|
-
customMetadata: options.customMetadata
|
|
2219
|
-
});
|
|
2127
|
+
}
|
|
2128
|
+
function googleCloudStorageAccessorFile(storage$1, storagePath) {
|
|
2129
|
+
const file = googleCloudStorageFileForStorageFilePath(storage$1, storagePath);
|
|
2130
|
+
function makeDownloadOptions(maxDownloadSizeBytes) {
|
|
2131
|
+
return {
|
|
2132
|
+
...(maxDownloadSizeBytes
|
|
2133
|
+
? {
|
|
2134
|
+
// end is inclusive
|
|
2135
|
+
end: maxDownloadSizeBytes - 1
|
|
2136
|
+
}
|
|
2137
|
+
: undefined)
|
|
2138
|
+
};
|
|
2220
2139
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2140
|
+
function _configureMetadata(options) {
|
|
2141
|
+
const customMetadata = util.filterUndefinedValues({
|
|
2142
|
+
...options.metadata?.customMetadata,
|
|
2143
|
+
...options?.customMetadata
|
|
2144
|
+
});
|
|
2145
|
+
return util.filterUndefinedValues({
|
|
2146
|
+
cacheControl: options.metadata?.cacheControl,
|
|
2147
|
+
contentDisposition: options.metadata?.contentDisposition,
|
|
2148
|
+
contentEncoding: options.metadata?.contentEncoding,
|
|
2149
|
+
contentLanguage: options.metadata?.contentLanguage,
|
|
2150
|
+
contentType: options.metadata?.contentType,
|
|
2151
|
+
metadata: !util.objectHasNoKeys(customMetadata) ? customMetadata : undefined
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
function makeUploadOptions(options) {
|
|
2155
|
+
let metadata;
|
|
2156
|
+
if (options != null) {
|
|
2157
|
+
metadata = _configureMetadata({
|
|
2158
|
+
metadata: {
|
|
2159
|
+
...options.metadata,
|
|
2160
|
+
contentType: options.contentType ?? options.metadata?.contentType
|
|
2161
|
+
},
|
|
2162
|
+
customMetadata: options.customMetadata
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
return {
|
|
2166
|
+
// non-resumable
|
|
2167
|
+
resumable: false,
|
|
2168
|
+
// add content type and other custom metadata
|
|
2169
|
+
...(metadata ? { metadata } : undefined)
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
function asFileMetadata(metadata) {
|
|
2173
|
+
return _configureMetadata({ metadata });
|
|
2174
|
+
}
|
|
2175
|
+
function makeStoragePathForPath(newPath) {
|
|
2176
|
+
let path;
|
|
2177
|
+
if (typeof newPath === 'string') {
|
|
2178
|
+
path = {
|
|
2179
|
+
bucketId: file.bucket.name,
|
|
2180
|
+
pathString: newPath
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
else {
|
|
2184
|
+
path = newPath;
|
|
2185
|
+
}
|
|
2186
|
+
return path;
|
|
2187
|
+
}
|
|
2188
|
+
async function copy(newPath, options) {
|
|
2189
|
+
const newStoragePath = makeStoragePathForPath(newPath);
|
|
2190
|
+
const newFile = googleCloudStorageAccessorFile(storage$1, newStoragePath);
|
|
2191
|
+
return _copyWithFile(newFile, options);
|
|
2192
|
+
}
|
|
2193
|
+
async function _copyWithFile(newFile, options) {
|
|
2194
|
+
const copyOptions = {
|
|
2195
|
+
...options
|
|
2196
|
+
};
|
|
2197
|
+
await file.copy(newFile.reference, copyOptions);
|
|
2198
|
+
return newFile;
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Configuration for the public ACL.
|
|
2202
|
+
*/
|
|
2203
|
+
const PUBLIC_ACL = {
|
|
2204
|
+
entity: 'allUsers',
|
|
2205
|
+
role: 'READER'
|
|
2228
2206
|
};
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2207
|
+
const accessorFile = {
|
|
2208
|
+
reference: file,
|
|
2209
|
+
storagePath,
|
|
2210
|
+
exists: () => file.exists().then((x) => x[0]),
|
|
2211
|
+
getDownloadUrl: () => file.getMetadata().then(() => file.publicUrl()),
|
|
2212
|
+
getSignedUrl: async (input) => {
|
|
2213
|
+
const expires = input?.expiresAt ??
|
|
2214
|
+
(input?.expiresIn != null
|
|
2215
|
+
? dateFns.addMilliseconds(new Date(), input.expiresIn) // use expiresIn if provided
|
|
2216
|
+
: dateFns.addHours(new Date(), 1)); // default expiration in 1 hour
|
|
2217
|
+
const config = {
|
|
2218
|
+
...input,
|
|
2219
|
+
action: input?.action ?? 'read',
|
|
2220
|
+
expires,
|
|
2221
|
+
expiresIn: undefined, // clear from input
|
|
2222
|
+
expiresAt: undefined
|
|
2223
|
+
};
|
|
2224
|
+
return file
|
|
2225
|
+
.getSignedUrl(config)
|
|
2226
|
+
.then((x) => x[0])
|
|
2227
|
+
.catch((e) => {
|
|
2228
|
+
let publicUrlBackup;
|
|
2229
|
+
if (e && e.name === 'SigningError' && (nestjs.isTestNodeEnv() || process.env.FIREBASE_STORAGE_EMULATOR_HOST)) {
|
|
2230
|
+
// NOTE: Signing does not behave properly in the emulator as it is not supported.
|
|
2231
|
+
// https://github.com/firebase/firebase-tools/issues/3400
|
|
2232
|
+
// we can return the public url instead.
|
|
2233
|
+
// This is fine, as in production this file url is protected by ACLs anyways.
|
|
2234
|
+
publicUrlBackup = file.publicUrl();
|
|
2235
|
+
}
|
|
2236
|
+
else {
|
|
2237
|
+
throw e;
|
|
2238
|
+
}
|
|
2239
|
+
return publicUrlBackup;
|
|
2240
|
+
});
|
|
2241
|
+
},
|
|
2242
|
+
getMetadata: () => file.getMetadata().then((x) => googleCloudFileMetadataToStorageMetadata(file, x[0])),
|
|
2243
|
+
setMetadata: (metadata) => file.setMetadata(asFileMetadata(metadata)).then((x) => googleCloudFileMetadataToStorageMetadata(file, x[0])),
|
|
2244
|
+
getBytes: (maxDownloadSizeBytes) => file.download(makeDownloadOptions(maxDownloadSizeBytes)).then((x) => x[0]),
|
|
2245
|
+
getStream: (maxDownloadSizeBytes) => file.createReadStream(makeDownloadOptions(maxDownloadSizeBytes)),
|
|
2246
|
+
upload: async (input, options) => {
|
|
2247
|
+
let dataToUpload;
|
|
2248
|
+
if (typeof input === 'string') {
|
|
2249
|
+
const parsedStringFormat = firebase.assertStorageUploadOptionsStringFormat(options);
|
|
2250
|
+
const stringFormat = parsedStringFormat === 'raw' ? 'utf-8' : parsedStringFormat;
|
|
2251
|
+
if (stringFormat === 'data_url') {
|
|
2252
|
+
// TODO(FUTURE): support this later if necessary. Server should really never see this type.
|
|
2253
|
+
throw new Error('"data_url" is unsupported.');
|
|
2254
|
+
}
|
|
2255
|
+
dataToUpload = Buffer.from(input, stringFormat);
|
|
2256
|
+
}
|
|
2257
|
+
else {
|
|
2258
|
+
if (Buffer.isBuffer(input)) {
|
|
2259
|
+
dataToUpload = input;
|
|
2260
|
+
}
|
|
2261
|
+
else if (types.isUint8Array(input)) {
|
|
2262
|
+
dataToUpload = Buffer.from(input);
|
|
2263
|
+
}
|
|
2264
|
+
else {
|
|
2265
|
+
// NOTE: these values shouldn't ever be encountered in the NodeJS environment. May remove later.
|
|
2266
|
+
if (types.isArrayBuffer(input)) {
|
|
2267
|
+
dataToUpload = Buffer.from(input);
|
|
2268
|
+
}
|
|
2269
|
+
else {
|
|
2270
|
+
dataToUpload = input.arrayBuffer().then((x) => Buffer.from(x));
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
const data = await dataToUpload;
|
|
2275
|
+
return file.save(data, makeUploadOptions(options));
|
|
2276
|
+
},
|
|
2277
|
+
uploadStream: (options) => file.createWriteStream(makeUploadOptions(options)),
|
|
2278
|
+
move: async (newPath, options) => {
|
|
2279
|
+
const newStoragePath = makeStoragePathForPath(newPath);
|
|
2280
|
+
const newFile = googleCloudStorageAccessorFile(storage$1, newStoragePath);
|
|
2281
|
+
const moveOptions = {
|
|
2282
|
+
...options
|
|
2283
|
+
};
|
|
2284
|
+
await file.moveFileAtomic(newFile.reference, moveOptions).catch(async (e) => {
|
|
2285
|
+
if (e instanceof storage.ApiError && e.response?.statusMessage === 'Not Implemented') {
|
|
2286
|
+
// NOTE: This is not implemented in storage emulator, so it will fail with this error in testing.
|
|
2287
|
+
// https://github.com/firebase/firebase-tools/issues/3751
|
|
2288
|
+
// we can perform the same task using copy and then deleting this file.
|
|
2289
|
+
await copy(newPath, moveOptions);
|
|
2290
|
+
await accessorFile.delete();
|
|
2291
|
+
}
|
|
2292
|
+
else {
|
|
2293
|
+
throw e;
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
return newFile;
|
|
2297
|
+
},
|
|
2298
|
+
copy,
|
|
2299
|
+
delete: (options) => file.delete(options).then((x) => undefined),
|
|
2300
|
+
isPublic: () => file.isPublic().then((x) => x[0]),
|
|
2301
|
+
makePublic: (setPublic) => (setPublic !== false ? file.acl.add(PUBLIC_ACL) : file.acl.delete({ entity: PUBLIC_ACL.entity })).then(() => undefined),
|
|
2302
|
+
makePrivate: (options) => file.makePrivate(options).then(() => undefined),
|
|
2303
|
+
getAcls: (options) => file.acl.get(options).then((x) => ({ acls: x[0], metadata: x[1] }))
|
|
2255
2304
|
};
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
*/
|
|
2262
|
-
const PUBLIC_ACL = {
|
|
2263
|
-
entity: 'allUsers',
|
|
2264
|
-
role: 'READER'
|
|
2265
|
-
};
|
|
2266
|
-
const accessorFile = {
|
|
2267
|
-
reference: file,
|
|
2268
|
-
storagePath,
|
|
2269
|
-
exists: () => file.exists().then(x => x[0]),
|
|
2270
|
-
getDownloadUrl: () => file.getMetadata().then(() => file.publicUrl()),
|
|
2271
|
-
getSignedUrl: async input => {
|
|
2272
|
-
const expires = input?.expiresAt ?? (input?.expiresIn != null ? dateFns.addMilliseconds(new Date(), input.expiresIn) // use expiresIn if provided
|
|
2273
|
-
: dateFns.addHours(new Date(), 1)); // default expiration in 1 hour
|
|
2274
|
-
const config = {
|
|
2275
|
-
...input,
|
|
2276
|
-
action: input?.action ?? 'read',
|
|
2277
|
-
expires,
|
|
2278
|
-
expiresIn: undefined,
|
|
2279
|
-
// clear from input
|
|
2280
|
-
expiresAt: undefined
|
|
2281
|
-
};
|
|
2282
|
-
return file.getSignedUrl(config).then(x => x[0]).catch(e => {
|
|
2283
|
-
let publicUrlBackup;
|
|
2284
|
-
if (e && e.name === 'SigningError' && (nestjs.isTestNodeEnv() || process.env.FIREBASE_STORAGE_EMULATOR_HOST)) {
|
|
2285
|
-
// NOTE: Signing does not behave properly in the emulator as it is not supported.
|
|
2286
|
-
// https://github.com/firebase/firebase-tools/issues/3400
|
|
2287
|
-
// we can return the public url instead.
|
|
2288
|
-
// This is fine, as in production this file url is protected by ACLs anyways.
|
|
2289
|
-
publicUrlBackup = file.publicUrl();
|
|
2290
|
-
} else {
|
|
2291
|
-
throw e;
|
|
2292
|
-
}
|
|
2293
|
-
return publicUrlBackup;
|
|
2294
|
-
});
|
|
2305
|
+
return accessorFile;
|
|
2306
|
+
}
|
|
2307
|
+
const googleCloudStorageListFilesResultFactory = firebase.storageListFilesResultFactory({
|
|
2308
|
+
hasItems(result) {
|
|
2309
|
+
return Boolean(result.apiResponse.items || result.apiResponse.prefixes);
|
|
2295
2310
|
},
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
getBytes: maxDownloadSizeBytes => file.download(makeDownloadOptions(maxDownloadSizeBytes)).then(x => x[0]),
|
|
2299
|
-
getStream: maxDownloadSizeBytes => file.createReadStream(makeDownloadOptions(maxDownloadSizeBytes)),
|
|
2300
|
-
upload: async (input, options) => {
|
|
2301
|
-
let dataToUpload;
|
|
2302
|
-
if (typeof input === 'string') {
|
|
2303
|
-
const parsedStringFormat = firebase.assertStorageUploadOptionsStringFormat(options);
|
|
2304
|
-
const stringFormat = parsedStringFormat === 'raw' ? 'utf-8' : parsedStringFormat;
|
|
2305
|
-
if (stringFormat === 'data_url') {
|
|
2306
|
-
// TODO(FUTURE): support this later if necessary. Server should really never see this type.
|
|
2307
|
-
throw new Error('"data_url" is unsupported.');
|
|
2308
|
-
}
|
|
2309
|
-
dataToUpload = Buffer.from(input, stringFormat);
|
|
2310
|
-
} else {
|
|
2311
|
-
if (Buffer.isBuffer(input)) {
|
|
2312
|
-
dataToUpload = input;
|
|
2313
|
-
} else if (types.isUint8Array(input)) {
|
|
2314
|
-
dataToUpload = Buffer.from(input);
|
|
2315
|
-
} else {
|
|
2316
|
-
// NOTE: these values shouldn't ever be encountered in the NodeJS environment. May remove later.
|
|
2317
|
-
if (types.isArrayBuffer(input)) {
|
|
2318
|
-
dataToUpload = Buffer.from(input);
|
|
2319
|
-
} else {
|
|
2320
|
-
dataToUpload = input.arrayBuffer().then(x => Buffer.from(x));
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
const data = await dataToUpload;
|
|
2325
|
-
return file.save(data, makeUploadOptions(options));
|
|
2311
|
+
hasNext: (result) => {
|
|
2312
|
+
return result.nextQuery != null;
|
|
2326
2313
|
},
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
const newStoragePath = makeStoragePathForPath(newPath);
|
|
2330
|
-
const newFile = googleCloudStorageAccessorFile(storage$1, newStoragePath);
|
|
2331
|
-
const moveOptions = {
|
|
2332
|
-
...options
|
|
2333
|
-
};
|
|
2334
|
-
await file.moveFileAtomic(newFile.reference, moveOptions).catch(async e => {
|
|
2335
|
-
if (e instanceof storage.ApiError && e.response?.statusMessage === 'Not Implemented') {
|
|
2336
|
-
// NOTE: This is not implemented in storage emulator, so it will fail with this error in testing.
|
|
2337
|
-
// https://github.com/firebase/firebase-tools/issues/3751
|
|
2338
|
-
// we can perform the same task using copy and then deleting this file.
|
|
2339
|
-
await copy(newPath, moveOptions);
|
|
2340
|
-
await accessorFile.delete();
|
|
2341
|
-
} else {
|
|
2342
|
-
throw e;
|
|
2343
|
-
}
|
|
2344
|
-
});
|
|
2345
|
-
return newFile;
|
|
2314
|
+
nextPageTokenFromResult(result) {
|
|
2315
|
+
return result.nextQuery?.pageToken;
|
|
2346
2316
|
},
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
const
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
},
|
|
2365
|
-
hasNext: result => {
|
|
2366
|
-
return result.nextQuery != null;
|
|
2367
|
-
},
|
|
2368
|
-
nextPageTokenFromResult(result) {
|
|
2369
|
-
return result.nextQuery?.pageToken;
|
|
2370
|
-
},
|
|
2371
|
-
next(storage, options, folder, result) {
|
|
2372
|
-
return folder.list({
|
|
2373
|
-
...options,
|
|
2374
|
-
...result.nextQuery
|
|
2375
|
-
});
|
|
2376
|
-
},
|
|
2377
|
-
file(storage, fileResult) {
|
|
2378
|
-
return googleCloudStorageAccessorFile(storage, fileResult.storagePath);
|
|
2379
|
-
},
|
|
2380
|
-
folder(storage, folderResult) {
|
|
2381
|
-
return googleCloudStorageAccessorFolder(storage, folderResult.storagePath);
|
|
2382
|
-
},
|
|
2383
|
-
filesFromResult(result) {
|
|
2384
|
-
const items = result.apiResponse?.items ?? [];
|
|
2385
|
-
return items.map(x => ({
|
|
2386
|
-
raw: x,
|
|
2387
|
-
name: util.slashPathName(x.name),
|
|
2388
|
-
storagePath: {
|
|
2389
|
-
bucketId: x.bucket,
|
|
2390
|
-
pathString: x.name
|
|
2391
|
-
}
|
|
2392
|
-
}));
|
|
2393
|
-
},
|
|
2394
|
-
foldersFromResult(result, folder) {
|
|
2395
|
-
const items = result.apiResponse?.prefixes ?? [];
|
|
2396
|
-
return items.map(prefix => ({
|
|
2397
|
-
raw: prefix,
|
|
2398
|
-
name: util.slashPathName(prefix),
|
|
2399
|
-
storagePath: {
|
|
2400
|
-
bucketId: folder.storagePath.bucketId,
|
|
2401
|
-
pathString: prefix
|
|
2402
|
-
}
|
|
2403
|
-
}));
|
|
2404
|
-
}
|
|
2317
|
+
next(storage, options, folder, result) {
|
|
2318
|
+
return folder.list({ ...options, ...result.nextQuery });
|
|
2319
|
+
},
|
|
2320
|
+
file(storage, fileResult) {
|
|
2321
|
+
return googleCloudStorageAccessorFile(storage, fileResult.storagePath);
|
|
2322
|
+
},
|
|
2323
|
+
folder(storage, folderResult) {
|
|
2324
|
+
return googleCloudStorageAccessorFolder(storage, folderResult.storagePath);
|
|
2325
|
+
},
|
|
2326
|
+
filesFromResult(result) {
|
|
2327
|
+
const items = result.apiResponse?.items ?? [];
|
|
2328
|
+
return items.map((x) => ({ raw: x, name: util.slashPathName(x.name), storagePath: { bucketId: x.bucket, pathString: x.name } }));
|
|
2329
|
+
},
|
|
2330
|
+
foldersFromResult(result, folder) {
|
|
2331
|
+
const items = result.apiResponse?.prefixes ?? [];
|
|
2332
|
+
return items.map((prefix) => ({ raw: prefix, name: util.slashPathName(prefix), storagePath: { bucketId: folder.storagePath.bucketId, pathString: prefix } }));
|
|
2333
|
+
}
|
|
2405
2334
|
});
|
|
2406
2335
|
function googleCloudStorageAccessorFolder(storage, storagePath) {
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
});
|
|
2445
|
-
}
|
|
2446
|
-
};
|
|
2447
|
-
return folder;
|
|
2336
|
+
const bucket = googleCloudStorageBucketForStorageFilePath(storage, storagePath);
|
|
2337
|
+
const file = bucket.file(storagePath.pathString);
|
|
2338
|
+
const folder = {
|
|
2339
|
+
reference: file,
|
|
2340
|
+
storagePath,
|
|
2341
|
+
exists: async () => folder.list({ maxResults: 1 }).then((x) => x.hasItems()),
|
|
2342
|
+
list: (options) => {
|
|
2343
|
+
const { maxResults, pageToken, includeNestedResults: listAll } = options ?? {};
|
|
2344
|
+
const listOptions = {
|
|
2345
|
+
maxResults,
|
|
2346
|
+
pageToken,
|
|
2347
|
+
autoPaginate: false,
|
|
2348
|
+
versions: false,
|
|
2349
|
+
...(listAll
|
|
2350
|
+
? {
|
|
2351
|
+
prefix: util.toRelativeSlashPathStartType(util.fixMultiSlashesInSlashPath(storagePath.pathString + '/'))
|
|
2352
|
+
}
|
|
2353
|
+
: {
|
|
2354
|
+
// includeTrailingDelimiter: true,
|
|
2355
|
+
delimiter: util.SLASH_PATH_SEPARATOR,
|
|
2356
|
+
prefix: util.toRelativeSlashPathStartType(util.fixMultiSlashesInSlashPath(storagePath.pathString + '/')) // make sure the folder always ends with a slash
|
|
2357
|
+
})
|
|
2358
|
+
};
|
|
2359
|
+
return bucket.getFiles(listOptions).then((x) => {
|
|
2360
|
+
const files = x[0];
|
|
2361
|
+
const nextQuery = x[1];
|
|
2362
|
+
const apiResponse = x[2];
|
|
2363
|
+
const result = {
|
|
2364
|
+
files: files,
|
|
2365
|
+
nextQuery,
|
|
2366
|
+
apiResponse: apiResponse
|
|
2367
|
+
};
|
|
2368
|
+
return googleCloudStorageListFilesResultFactory(storage, folder, options, result);
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
return folder;
|
|
2448
2373
|
}
|
|
2449
2374
|
function googleCloudStorageFirebaseStorageAccessorDriver() {
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2375
|
+
return {
|
|
2376
|
+
type: 'server',
|
|
2377
|
+
file: (storage, path) => googleCloudStorageAccessorFile(storage, path),
|
|
2378
|
+
folder: (storage, path) => googleCloudStorageAccessorFolder(storage, path)
|
|
2379
|
+
};
|
|
2455
2380
|
}
|
|
2456
2381
|
|
|
2457
2382
|
function googleCloudFirebaseStorageDrivers() {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2383
|
+
return {
|
|
2384
|
+
storageDriverIdentifier: '@google-cloud/storage',
|
|
2385
|
+
storageDriverType: 'production',
|
|
2386
|
+
storageAccessorDriver: googleCloudStorageFirebaseStorageAccessorDriver()
|
|
2387
|
+
};
|
|
2463
2388
|
}
|
|
2464
2389
|
|
|
2465
2390
|
/**
|
|
2466
2391
|
* Basic service that implements FirebaseStorageAccessor and provides a FirebaseStorageContext.
|
|
2467
2392
|
*/
|
|
2468
2393
|
class FirebaseServerStorageService {
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2394
|
+
_storageContext;
|
|
2395
|
+
constructor(storageContext) {
|
|
2396
|
+
this._storageContext = storageContext;
|
|
2397
|
+
}
|
|
2398
|
+
get storageContext() {
|
|
2399
|
+
return this._storageContext;
|
|
2400
|
+
}
|
|
2401
|
+
defaultBucket() {
|
|
2402
|
+
return this.storageContext.defaultBucket();
|
|
2403
|
+
}
|
|
2404
|
+
file(path) {
|
|
2405
|
+
return this.storageContext.file(path);
|
|
2406
|
+
}
|
|
2407
|
+
folder(path) {
|
|
2408
|
+
return this.storageContext.folder(path);
|
|
2409
|
+
}
|
|
2485
2410
|
}
|
|
2486
2411
|
|
|
2487
2412
|
/**
|
|
@@ -2495,7 +2420,7 @@ const googleCloudFirebaseStorageContextFactory = firebase.firebaseStorageContext
|
|
|
2495
2420
|
* @returns
|
|
2496
2421
|
*/
|
|
2497
2422
|
function googleCloudStorageFromFirebaseAdminStorage(storage) {
|
|
2498
|
-
|
|
2423
|
+
return storage.storageClient;
|
|
2499
2424
|
}
|
|
2500
2425
|
|
|
2501
2426
|
// MARK: Tokens
|
|
@@ -2514,59 +2439,69 @@ const FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN = 'FIREBASE_STORAGE_CONTEXT_
|
|
|
2514
2439
|
/**
|
|
2515
2440
|
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
2516
2441
|
*/
|
|
2517
|
-
exports.FirebaseServerStorageModule = class FirebaseServerStorageModule {
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2442
|
+
exports.FirebaseServerStorageModule = class FirebaseServerStorageModule {
|
|
2443
|
+
};
|
|
2444
|
+
exports.FirebaseServerStorageModule = __decorate([
|
|
2445
|
+
common.Module({
|
|
2446
|
+
providers: [
|
|
2447
|
+
{
|
|
2448
|
+
provide: FIREBASE_STORAGE_TOKEN,
|
|
2449
|
+
useFactory: (app) => googleCloudStorageFromFirebaseAdminStorage(app.storage()),
|
|
2450
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
2451
|
+
}
|
|
2452
|
+
],
|
|
2453
|
+
exports: [FIREBASE_STORAGE_TOKEN]
|
|
2454
|
+
})
|
|
2455
|
+
], exports.FirebaseServerStorageModule);
|
|
2526
2456
|
/**
|
|
2527
2457
|
* Nest provider module for firebase that includes the FirebaseServerStorageModule and provides a value for STORAGE_CONTEXT_TOKEN using the googleCloudStorageContextFactory.
|
|
2528
2458
|
*/
|
|
2529
|
-
exports.FirebaseServerStorageContextModule = class FirebaseServerStorageContextModule {
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2459
|
+
exports.FirebaseServerStorageContextModule = class FirebaseServerStorageContextModule {
|
|
2460
|
+
};
|
|
2461
|
+
exports.FirebaseServerStorageContextModule = __decorate([
|
|
2462
|
+
common.Module({
|
|
2463
|
+
imports: [exports.FirebaseServerStorageModule],
|
|
2464
|
+
providers: [
|
|
2465
|
+
{
|
|
2466
|
+
provide: FIREBASE_STORAGE_CONTEXT_TOKEN,
|
|
2467
|
+
useFactory: googleCloudFirebaseStorageContextFactory,
|
|
2468
|
+
inject: [FIREBASE_STORAGE_TOKEN, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN]
|
|
2469
|
+
}
|
|
2470
|
+
],
|
|
2471
|
+
exports: [exports.FirebaseServerStorageModule, FIREBASE_STORAGE_CONTEXT_TOKEN]
|
|
2472
|
+
})
|
|
2473
|
+
], exports.FirebaseServerStorageContextModule);
|
|
2539
2474
|
// MARK: Token Configuration
|
|
2540
2475
|
function firebaseServerStorageDefaultBucketIdTokenProvider(input) {
|
|
2541
|
-
|
|
2542
|
-
defaultBucketId
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
useValue: config
|
|
2550
|
-
};
|
|
2476
|
+
const config = typeof input === 'string' ? { defaultBucketId: input } : input;
|
|
2477
|
+
if (!config.defaultBucketId) {
|
|
2478
|
+
throw new Error('Non-empty defaultBucketId is required.');
|
|
2479
|
+
}
|
|
2480
|
+
return {
|
|
2481
|
+
provide: FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN,
|
|
2482
|
+
useValue: config
|
|
2483
|
+
};
|
|
2551
2484
|
}
|
|
2552
2485
|
function defaultProvideFirebaseServerStorageServiceSimple() {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2486
|
+
return {
|
|
2487
|
+
provide: FirebaseServerStorageService,
|
|
2488
|
+
useFactory: (context) => new FirebaseServerStorageService(context)
|
|
2489
|
+
};
|
|
2557
2490
|
}
|
|
2558
2491
|
function provideFirebaseServerStorageService(provider) {
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2492
|
+
const providers = [
|
|
2493
|
+
{
|
|
2494
|
+
...provider,
|
|
2495
|
+
inject: provider.inject ?? [FIREBASE_STORAGE_CONTEXT_TOKEN]
|
|
2496
|
+
}
|
|
2497
|
+
];
|
|
2498
|
+
if (provider.provide !== FirebaseServerStorageService) {
|
|
2499
|
+
providers.push({
|
|
2500
|
+
provide: FirebaseServerStorageService,
|
|
2501
|
+
useExisting: provider.provide
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
return providers;
|
|
2570
2505
|
}
|
|
2571
2506
|
/**
|
|
2572
2507
|
* Convenience function used to generate ModuleMetadata for an app's Auth related modules and FirebaseServerStorageService provider.
|
|
@@ -2576,145 +2511,133 @@ function provideFirebaseServerStorageService(provider) {
|
|
|
2576
2511
|
* @returns
|
|
2577
2512
|
*/
|
|
2578
2513
|
function firebaseServerStorageModuleMetadata(config) {
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2514
|
+
const serviceProvider = config && config.serviceProvider ? config.serviceProvider : defaultProvideFirebaseServerStorageServiceSimple();
|
|
2515
|
+
const providers = provideFirebaseServerStorageService(serviceProvider);
|
|
2516
|
+
const tokensToExport = nestjs.injectionTokensFromProviders(providers);
|
|
2517
|
+
return nestjs.mergeModuleMetadata({
|
|
2518
|
+
imports: [exports.FirebaseServerStorageContextModule],
|
|
2519
|
+
exports: [exports.FirebaseServerStorageContextModule, ...tokensToExport],
|
|
2520
|
+
providers
|
|
2521
|
+
}, config);
|
|
2587
2522
|
}
|
|
2588
2523
|
|
|
2589
|
-
class FirebaseNestServerRootModule {
|
|
2524
|
+
class FirebaseNestServerRootModule {
|
|
2525
|
+
}
|
|
2590
2526
|
function nestServerInstance(config) {
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2527
|
+
const { moduleClass, providers: additionalProviders, defaultStorageBucket: inputDefaultStorageBucket, forceStorageBucket, globalApiRoutePrefix, configureNestServerInstance } = config;
|
|
2528
|
+
const serversCache = new Map();
|
|
2529
|
+
const initNestServer = (firebaseApp, env) => {
|
|
2530
|
+
const appName = firebaseApp.name;
|
|
2531
|
+
const defaultStorageBucket = inputDefaultStorageBucket ?? firebaseApp.options.storageBucket;
|
|
2532
|
+
let nestServer = serversCache.get(appName);
|
|
2533
|
+
if (!nestServer) {
|
|
2534
|
+
const server = express();
|
|
2535
|
+
const createNestServer = async (expressInstance) => {
|
|
2536
|
+
const providers = [firebaseServerAppTokenProvider(util.asGetter(firebaseApp))];
|
|
2537
|
+
// configure environment providers
|
|
2538
|
+
if (env?.environment != null) {
|
|
2539
|
+
providers.push(nestjs.serverEnvTokenProvider(env.environment));
|
|
2540
|
+
if (config.configureEnvService !== false) {
|
|
2541
|
+
providers.push({
|
|
2542
|
+
provide: FirebaseServerEnvService,
|
|
2543
|
+
useClass: exports.DefaultFirebaseServerEnvService
|
|
2544
|
+
}, {
|
|
2545
|
+
provide: nestjs.ServerEnvironmentService,
|
|
2546
|
+
useExisting: FirebaseServerEnvService
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
if (additionalProviders) {
|
|
2551
|
+
util.pushItemOrArrayItemsIntoArray(providers, additionalProviders);
|
|
2552
|
+
}
|
|
2553
|
+
const imports = [moduleClass];
|
|
2554
|
+
// NOTE: https://cloud.google.com/functions/docs/writing/http#parsing_http_requests
|
|
2555
|
+
const options = { bodyParser: false }; // firebase already parses the requests
|
|
2556
|
+
if (config.configureWebhooks) {
|
|
2557
|
+
imports.push(exports.ConfigureFirebaseWebhookMiddlewareModule);
|
|
2558
|
+
}
|
|
2559
|
+
if (config.appCheckEnabled != false) {
|
|
2560
|
+
imports.push(exports.ConfigureFirebaseAppCheckMiddlewareModule);
|
|
2561
|
+
}
|
|
2562
|
+
if (defaultStorageBucket) {
|
|
2563
|
+
providers.push(firebaseServerStorageDefaultBucketIdTokenProvider({
|
|
2564
|
+
defaultBucketId: defaultStorageBucket,
|
|
2565
|
+
forceBucket: forceStorageBucket
|
|
2566
|
+
}));
|
|
2567
|
+
}
|
|
2568
|
+
// provide the global prefix config to the app
|
|
2569
|
+
providers.push({
|
|
2570
|
+
provide: GlobalRoutePrefixConfig,
|
|
2571
|
+
useValue: {
|
|
2572
|
+
globalApiRoutePrefix
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
const providersModule = {
|
|
2576
|
+
module: FirebaseNestServerRootModule,
|
|
2577
|
+
imports,
|
|
2578
|
+
providers,
|
|
2579
|
+
exports: providers,
|
|
2580
|
+
global: true
|
|
2581
|
+
};
|
|
2582
|
+
let nestApp = await core.NestFactory.create(providersModule, new platformExpress.ExpressAdapter(expressInstance), options);
|
|
2583
|
+
if (globalApiRoutePrefix) {
|
|
2584
|
+
nestApp = nestApp.setGlobalPrefix(globalApiRoutePrefix);
|
|
2585
|
+
}
|
|
2586
|
+
if (configureNestServerInstance) {
|
|
2587
|
+
nestApp = configureNestServerInstance(nestApp) || nestApp;
|
|
2588
|
+
}
|
|
2589
|
+
return nestApp.init();
|
|
2590
|
+
};
|
|
2591
|
+
const nest = createNestServer(server).catch((err) => {
|
|
2592
|
+
console.error('Nest failed startup.', err);
|
|
2593
|
+
throw err;
|
|
2618
2594
|
});
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
if (additionalProviders) {
|
|
2622
|
-
util.pushItemOrArrayItemsIntoArray(providers, additionalProviders);
|
|
2623
|
-
}
|
|
2624
|
-
const imports = [moduleClass];
|
|
2625
|
-
// NOTE: https://cloud.google.com/functions/docs/writing/http#parsing_http_requests
|
|
2626
|
-
const options = {
|
|
2627
|
-
bodyParser: false
|
|
2628
|
-
}; // firebase already parses the requests
|
|
2629
|
-
if (config.configureWebhooks) {
|
|
2630
|
-
imports.push(exports.ConfigureFirebaseWebhookMiddlewareModule);
|
|
2595
|
+
nestServer = { server, nest: util.makeGetter(nest) };
|
|
2596
|
+
serversCache.set(appName, nestServer);
|
|
2631
2597
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2598
|
+
return nestServer;
|
|
2599
|
+
};
|
|
2600
|
+
const removeNestServer = async (firebaseApp) => {
|
|
2601
|
+
const appName = firebaseApp.name;
|
|
2602
|
+
const nestServer = serversCache.get(appName);
|
|
2603
|
+
let removed;
|
|
2604
|
+
if (nestServer) {
|
|
2605
|
+
removed = nestServer.nest().then((x) => {
|
|
2606
|
+
serversCache.delete(appName);
|
|
2607
|
+
return x.close().then(() => true);
|
|
2608
|
+
});
|
|
2634
2609
|
}
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
defaultBucketId: defaultStorageBucket,
|
|
2638
|
-
forceBucket: forceStorageBucket
|
|
2639
|
-
}));
|
|
2610
|
+
else {
|
|
2611
|
+
removed = Promise.resolve(false);
|
|
2640
2612
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
const providersModule = {
|
|
2649
|
-
module: FirebaseNestServerRootModule,
|
|
2650
|
-
imports,
|
|
2651
|
-
providers,
|
|
2652
|
-
exports: providers,
|
|
2653
|
-
global: true
|
|
2654
|
-
};
|
|
2655
|
-
let nestApp = await core.NestFactory.create(providersModule, new platformExpress.ExpressAdapter(expressInstance), options);
|
|
2656
|
-
if (globalApiRoutePrefix) {
|
|
2657
|
-
nestApp = nestApp.setGlobalPrefix(globalApiRoutePrefix);
|
|
2658
|
-
}
|
|
2659
|
-
if (configureNestServerInstance) {
|
|
2660
|
-
nestApp = configureNestServerInstance(nestApp) || nestApp;
|
|
2661
|
-
}
|
|
2662
|
-
return nestApp.init();
|
|
2663
|
-
};
|
|
2664
|
-
const nest = createNestServer(server).catch(err => {
|
|
2665
|
-
console.error('Nest failed startup.', err);
|
|
2666
|
-
throw err;
|
|
2667
|
-
});
|
|
2668
|
-
nestServer = {
|
|
2669
|
-
server,
|
|
2670
|
-
nest: util.makeGetter(nest)
|
|
2671
|
-
};
|
|
2672
|
-
serversCache.set(appName, nestServer);
|
|
2673
|
-
}
|
|
2674
|
-
return nestServer;
|
|
2675
|
-
};
|
|
2676
|
-
const removeNestServer = async firebaseApp => {
|
|
2677
|
-
const appName = firebaseApp.name;
|
|
2678
|
-
const nestServer = serversCache.get(appName);
|
|
2679
|
-
let removed;
|
|
2680
|
-
if (nestServer) {
|
|
2681
|
-
removed = nestServer.nest().then(x => {
|
|
2682
|
-
serversCache.delete(appName);
|
|
2683
|
-
return x.close().then(() => true);
|
|
2684
|
-
});
|
|
2685
|
-
} else {
|
|
2686
|
-
removed = Promise.resolve(false);
|
|
2687
|
-
}
|
|
2688
|
-
return removed;
|
|
2689
|
-
};
|
|
2690
|
-
return {
|
|
2691
|
-
moduleClass,
|
|
2692
|
-
initNestServer,
|
|
2693
|
-
removeNestServer
|
|
2694
|
-
};
|
|
2613
|
+
return removed;
|
|
2614
|
+
};
|
|
2615
|
+
return {
|
|
2616
|
+
moduleClass,
|
|
2617
|
+
initNestServer,
|
|
2618
|
+
removeNestServer
|
|
2619
|
+
};
|
|
2695
2620
|
}
|
|
2696
2621
|
|
|
2697
2622
|
/**
|
|
2698
2623
|
* Abstract class that wraps an INestApplicationContext value.
|
|
2699
2624
|
*/
|
|
2700
2625
|
class AbstractNestContext {
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2626
|
+
_nest;
|
|
2627
|
+
constructor(nest) {
|
|
2628
|
+
this._nest = nest;
|
|
2629
|
+
}
|
|
2630
|
+
get nest() {
|
|
2631
|
+
return this._nest;
|
|
2632
|
+
}
|
|
2708
2633
|
}
|
|
2709
2634
|
class AbstractFirebaseNestContext extends AbstractNestContext {
|
|
2710
|
-
constructor(...args) {
|
|
2711
|
-
super(...args);
|
|
2712
2635
|
/**
|
|
2713
2636
|
* FirebasePermissionErrorContextErrorFunction to use with makeModelContext().
|
|
2714
2637
|
*
|
|
2715
2638
|
* Defaults to nestFirebaseForbiddenPermissionError().
|
|
2716
2639
|
*/
|
|
2717
|
-
|
|
2640
|
+
makePermissionError = nestFirebaseForbiddenPermissionError;
|
|
2718
2641
|
/**
|
|
2719
2642
|
* FirebaseDoesNotExistErrorContextErrorFunction to use with makeModelContext().
|
|
2720
2643
|
*
|
|
@@ -2723,55 +2646,56 @@ class AbstractFirebaseNestContext extends AbstractNestContext {
|
|
|
2723
2646
|
* Some configurations may prefer to use nestFirebaseForbiddenPermissionError instead, which returns a forbidden error instead.
|
|
2724
2647
|
* This prevents the leaking of information about the existence of an object.
|
|
2725
2648
|
*/
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2649
|
+
makeDoesNotExistError = nestFirebaseDoesNotExistError;
|
|
2650
|
+
get envService() {
|
|
2651
|
+
return this.nest.get(FirebaseServerEnvService);
|
|
2652
|
+
}
|
|
2653
|
+
get storageService() {
|
|
2654
|
+
return this.nest.get(FirebaseServerStorageService);
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Creates a FirebaseAppModelContext instance.
|
|
2658
|
+
*
|
|
2659
|
+
* @param auth
|
|
2660
|
+
* @param buildFn
|
|
2661
|
+
* @returns
|
|
2662
|
+
*/
|
|
2663
|
+
makeModelContext(auth, buildFn) {
|
|
2664
|
+
const base = {
|
|
2665
|
+
auth: this.authService.authContextInfo(auth),
|
|
2666
|
+
app: this.app,
|
|
2667
|
+
makePermissionError: this.makePermissionError,
|
|
2668
|
+
makeDoesNotExistError: this.makeDoesNotExistError
|
|
2669
|
+
};
|
|
2670
|
+
return buildFn
|
|
2671
|
+
? util.build({
|
|
2672
|
+
base,
|
|
2673
|
+
build: buildFn
|
|
2674
|
+
})
|
|
2675
|
+
: base;
|
|
2676
|
+
}
|
|
2677
|
+
/**
|
|
2678
|
+
* Creates a InContextFirebaseModelsService given the input context and parameters.
|
|
2679
|
+
*
|
|
2680
|
+
* @param context
|
|
2681
|
+
* @param buildFn
|
|
2682
|
+
* @returns
|
|
2683
|
+
*/
|
|
2684
|
+
model(context, buildFn) {
|
|
2685
|
+
const firebaseModelContext = this.makeModelContext(context, buildFn);
|
|
2686
|
+
return firebase.inContextFirebaseModelsServiceFactory(this.firebaseModelsService)(firebaseModelContext);
|
|
2687
|
+
}
|
|
2688
|
+
async useModel(type, select) {
|
|
2689
|
+
const context = this.makeModelContext(select.request, select.buildFn);
|
|
2690
|
+
const usePromise = firebase.useFirebaseModelsService(this.firebaseModelsService, type, {
|
|
2691
|
+
context,
|
|
2692
|
+
key: select.key,
|
|
2693
|
+
roles: select.roles,
|
|
2694
|
+
rolesSetIncludes: select.rolesSetIncludes
|
|
2695
|
+
});
|
|
2696
|
+
const use = select.use ?? ((x) => x);
|
|
2697
|
+
return usePromise(use);
|
|
2698
|
+
}
|
|
2775
2699
|
}
|
|
2776
2700
|
|
|
2777
2701
|
exports.ALREADY_EXISTS_ERROR_CODE = ALREADY_EXISTS_ERROR_CODE;
|
|
@@ -2806,9 +2730,7 @@ exports.FirebaseServerStorageService = FirebaseServerStorageService;
|
|
|
2806
2730
|
exports.INTERNAL_SERVER_ERROR_CODE = INTERNAL_SERVER_ERROR_CODE;
|
|
2807
2731
|
exports.MODEL_NOT_AVAILABLE_ERROR_CODE = MODEL_NOT_AVAILABLE_ERROR_CODE;
|
|
2808
2732
|
exports.NOT_FOUND_ERROR_CODE = NOT_FOUND_ERROR_CODE;
|
|
2809
|
-
exports.NO_AUTH_ERROR_CODE = NO_AUTH_ERROR_CODE;
|
|
2810
2733
|
exports.NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE = NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE;
|
|
2811
|
-
exports.NO_UID_ERROR_CODE = NO_UID_ERROR_CODE;
|
|
2812
2734
|
exports.NoSetupContentFirebaseServerNewUserService = NoSetupContentFirebaseServerNewUserService;
|
|
2813
2735
|
exports.PERMISSION_DENIED_ERROR_CODE = PERMISSION_DENIED_ERROR_CODE;
|
|
2814
2736
|
exports.PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE = PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE;
|