@@ -31,11 +31,17 @@ const DATABASE_ADDRESS_ENV: string = 'FIREBASE_DATABASE_EMULATOR_HOST';
31
31
/** The default address for the local database emulator. */
32
32
const DATABASE_ADDRESS_DEFAULT : string = 'localhost:9000' ;
33
33
34
- /** If any of environment variable is set, use it for the Firestore emulator. */
34
+ /** If this environment variable is set, use it for the Firestore emulator. */
35
35
const FIRESTORE_ADDRESS_ENV : string = 'FIRESTORE_EMULATOR_HOST' ;
36
36
/** The default address for the local Firestore emulator. */
37
37
const FIRESTORE_ADDRESS_DEFAULT : string = 'localhost:8080' ;
38
38
39
+ /** If this environment variable is set, use it for the Storage emulator. */
40
+ const FIREBASE_STORAGE_ADDRESS_ENV : string = 'FIREBASE_STORAGE_EMULATOR_HOST' ;
41
+ const CLOUD_STORAGE_ADDRESS_ENV : string = 'STORAGE_EMULATOR_HOST' ;
42
+ /** The default address for the local Firestore emulator. */
43
+ const STORAGE_ADDRESS_DEFAULT : string = 'localhost:9199' ;
44
+
39
45
/** Environment variable to locate the Emulator Hub */
40
46
const HUB_HOST_ENV : string = 'FIREBASE_EMULATOR_HUB' ;
41
47
/** The default address for the Emulator Hub */
@@ -47,6 +53,9 @@ let _databaseHost: string | undefined = undefined;
47
53
/** The actual address for the Firestore emulator */
48
54
let _firestoreHost : string | undefined = undefined ;
49
55
56
+ /** The actual address for the Storage emulator */
57
+ let _storageHost : string | undefined = undefined ;
58
+
50
59
/** The actual address for the Emulator Hub */
51
60
let _hubHost : string | undefined = undefined ;
52
61
@@ -133,6 +142,10 @@ export type FirebaseEmulatorOptions = {
133
142
host : string ;
134
143
port : number ;
135
144
} ;
145
+ storage ?: {
146
+ host : string ;
147
+ port : number ;
148
+ } ;
136
149
hub ?: {
137
150
host : string ;
138
151
port : number ;
@@ -193,6 +206,7 @@ export function apps(): firebase.app.App[] {
193
206
export type AppOptions = {
194
207
databaseName ?: string ;
195
208
projectId ?: string ;
209
+ storageBucket ?: string ;
196
210
auth ?: TokenOptions ;
197
211
} ;
198
212
/** Construct an App authenticated with options.auth. */
@@ -201,19 +215,20 @@ export function initializeTestApp(options: AppOptions): firebase.app.App {
201
215
? createUnsecuredJwt ( options . auth , options . projectId )
202
216
: undefined ;
203
217
204
- return initializeApp ( jwt , options . databaseName , options . projectId ) ;
218
+ return initializeApp ( jwt , options . databaseName , options . projectId , options . storageBucket ) ;
205
219
}
206
220
207
221
export type AdminAppOptions = {
208
222
databaseName ?: string ;
209
223
projectId ?: string ;
224
+ storageBucket ?: string ;
210
225
} ;
211
226
/** Construct an App authenticated as an admin user. */
212
227
export function initializeAdminApp ( options : AdminAppOptions ) : firebase . app . App {
213
228
const admin = require ( 'firebase-admin' ) ;
214
229
215
230
const app = admin . initializeApp (
216
- getAppOptions ( options . databaseName , options . projectId ) ,
231
+ getAppOptions ( options . databaseName , options . projectId , options . storageBucket ) ,
217
232
getRandomAppName ( )
218
233
) ;
219
234
@@ -248,6 +263,10 @@ export function useEmulators(options: FirebaseEmulatorOptions): void {
248
263
_firestoreHost = getAddress ( options . firestore . host , options . firestore . port ) ;
249
264
}
250
265
266
+ if ( options . storage ) {
267
+ _storageHost = getAddress ( options . storage . host , options . storage . port ) ;
268
+ }
269
+
251
270
if ( options . hub ) {
252
271
_hubHost = getAddress ( options . hub . host , options . hub . port ) ;
253
272
}
@@ -301,6 +320,13 @@ export async function discoverEmulators(
301
320
} ;
302
321
}
303
322
323
+ if ( data . storage ) {
324
+ options . storage = {
325
+ host : data . storage . host ,
326
+ port : data . storage . port
327
+ } ;
328
+ }
329
+
304
330
if ( data . hub ) {
305
331
options . hub = {
306
332
host : data . hub . host ,
@@ -351,6 +377,25 @@ function getFirestoreHost() {
351
377
return _firestoreHost ;
352
378
}
353
379
380
+ function getStorageHost ( ) {
381
+ if ( ! _storageHost ) {
382
+ const fromEnv = process . env [ FIREBASE_STORAGE_ADDRESS_ENV ] || process . env [ CLOUD_STORAGE_ADDRESS_ENV ] ;
383
+ if ( fromEnv ) {
384
+ // The STORAGE_EMULATOR_HOST env var is an older Cloud Standard which includes http:// while
385
+ // the FIREBASE_STORAGE_EMULATOR_HOST is a newer variable supported beginning in the Admin
386
+ // SDK v9.7.0 which does not have the protocol.
387
+ _storageHost = fromEnv . replace ( "http://" , "" ) ;
388
+ } else {
389
+ console . warn (
390
+ `Warning: ${ FIREBASE_STORAGE_ADDRESS_ENV } not set, using default value ${ STORAGE_ADDRESS_DEFAULT } `
391
+ ) ;
392
+ _storageHost = STORAGE_ADDRESS_DEFAULT ;
393
+ }
394
+ }
395
+
396
+ return _storageHost ;
397
+ }
398
+
354
399
function getHubHost ( ) {
355
400
if ( ! _hubHost ) {
356
401
const fromEnv = process . env [ HUB_HOST_ENV ] ;
@@ -367,34 +412,53 @@ function getHubHost() {
367
412
return _hubHost ;
368
413
}
369
414
415
+ function parseHost ( host : string ) : { hostname : string , port : number } {
416
+ const u = new URL ( host ) ;
417
+ return {
418
+ hostname : u . hostname ,
419
+ port : Number . parseInt ( u . port , 10 )
420
+ }
421
+ }
422
+
370
423
function getRandomAppName ( ) : string {
371
424
return 'app-' + new Date ( ) . getTime ( ) + '-' + Math . random ( ) ;
372
425
}
373
426
427
+ function getDatabaseUrl ( databaseName : string ) {
428
+ return `http://${ getDatabaseHost ( ) } ?ns=${ databaseName } ` ;
429
+ }
430
+
374
431
function getAppOptions (
375
432
databaseName ?: string ,
376
- projectId ?: string
433
+ projectId ?: string ,
434
+ storageBucket ?: string ,
377
435
) : { [ key : string ] : string } {
378
436
let appOptions : { [ key : string ] : string } = { } ;
379
437
380
438
if ( databaseName ) {
381
439
appOptions [
382
440
'databaseURL'
383
- ] = `http:// ${ getDatabaseHost ( ) } ?ns= ${ databaseName } ` ;
441
+ ] = getDatabaseUrl ( databaseName ) ;
384
442
}
443
+
385
444
if ( projectId ) {
386
445
appOptions [ 'projectId' ] = projectId ;
387
446
}
388
447
448
+ if ( storageBucket ) {
449
+ appOptions [ 'storgeBucket' ] = storageBucket ;
450
+ }
451
+
389
452
return appOptions ;
390
453
}
391
454
392
455
function initializeApp (
393
456
accessToken ?: string ,
394
457
databaseName ?: string ,
395
- projectId ?: string
458
+ projectId ?: string ,
459
+ storageBucket ?: string ,
396
460
) : firebase . app . App {
397
- const appOptions = getAppOptions ( databaseName , projectId ) ;
461
+ const appOptions = getAppOptions ( databaseName , projectId , storageBucket ) ;
398
462
const app = firebase . initializeApp ( appOptions , getRandomAppName ( ) ) ;
399
463
if ( accessToken ) {
400
464
const mockAuthComponent = new Component (
@@ -417,17 +481,22 @@ function initializeApp(
417
481
) ;
418
482
}
419
483
if ( databaseName ) {
484
+ const { hostname, port } = parseHost ( getDatabaseHost ( ) ) ;
485
+ app . database ( ) . useEmulator ( hostname , port ) ;
486
+
420
487
// Toggle network connectivity to force a reauthentication attempt.
421
488
// This mitigates a minor race condition where the client can send the
422
489
// first database request before authenticating.
423
490
app . database ( ) . goOffline ( ) ;
424
491
app . database ( ) . goOnline ( ) ;
425
492
}
426
493
if ( projectId ) {
427
- app . firestore ( ) . settings ( {
428
- host : getFirestoreHost ( ) ,
429
- ssl : false
430
- } ) ;
494
+ const { hostname, port } = parseHost ( getFirestoreHost ( ) ) ;
495
+ app . firestore ( ) . useEmulator ( hostname , port ) ;
496
+ }
497
+ if ( storageBucket ) {
498
+ const { hostname, port } = parseHost ( getStorageHost ( ) ) ;
499
+ app . storage ( ) . useEmulator ( hostname , port ) ;
431
500
}
432
501
/**
433
502
Mute warnings for the previously-created database and whatever other
@@ -498,6 +567,31 @@ export async function loadFirestoreRules(
498
567
}
499
568
}
500
569
570
+ export type LoadStorageRulesOptions = {
571
+ rules : string ;
572
+ } ;
573
+ export async function loadStorageRules (
574
+ options : LoadStorageRulesOptions
575
+ ) : Promise < void > {
576
+ if ( ! options . rules ) {
577
+ throw new Error ( 'must provide rules to loadStorageRules' ) ;
578
+ }
579
+
580
+ const resp = await requestPromise ( request . put , {
581
+ method : 'PUT' ,
582
+ uri : `http://${ getStorageHost ( ) } /internal/setRules` ,
583
+ body : JSON . stringify ( {
584
+ rules : {
585
+ files : [ { name : 'storage.rules' , content : options . rules } ]
586
+ }
587
+ } )
588
+ } ) ;
589
+
590
+ if ( resp . statusCode !== 200 ) {
591
+ throw new Error ( JSON . parse ( resp . body . error ) ) ;
592
+ }
593
+ }
594
+
501
595
export type ClearFirestoreDataOptions = {
502
596
projectId : string ;
503
597
} ;
0 commit comments