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