Skip to content

Commit

Permalink
fix(storage): setup different emulator per bucket
Browse files Browse the repository at this point in the history
  • Loading branch information
russellwheatley authored and mikehardy committed Jul 14, 2024
1 parent a41e556 commit 66faa1d
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 15 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/scripts/storage.rules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /b/react-native-firebase-testing.appspot.com/o {
match /{document=**} {
allow read, write: if false;
}
Expand All @@ -18,4 +18,10 @@ service firebase.storage {
allow read, write: if true;
}
}

match /b/react-native-firebase-testing/o {
match /only-second-bucket/{document=**} {
allow read, write: if true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,15 @@ public void setMaxUploadRetryTime(String appName, double milliseconds, Promise p
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Storage#useEmulator
*/
@ReactMethod
public void useEmulator(String appName, String host, int port, Promise promise) {
public void useEmulator(String appName, String host, int port, String bucketUrl, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseStorage firebaseStorage = FirebaseStorage.getInstance(firebaseApp);
if (emulatorConfigs.get(appName) == null) {

FirebaseStorage firebaseStorage = FirebaseStorage.getInstance(firebaseApp, bucketUrl);
String emulatorKey = appName + ":" + bucketUrl;

if (emulatorConfigs.get(emulatorKey) == null) {
firebaseStorage.useEmulator(host, port);
emulatorConfigs.put(appName, "true");
emulatorConfigs.put(emulatorKey, "true");
}
promise.resolve(null);
}
Expand Down
41 changes: 41 additions & 0 deletions packages/storage/e2e/StorageReference.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,47 @@ describe('storage() -> StorageReference', function () {
await seed(PATH);
});

describe('second storage bucket writes to Storage emulator', function () {
let secondStorage;
// Same bucket defined in app.js when setting up emulator
const secondStorageBucket = 'gs://react-native-firebase-testing';

before(function () {
const { getStorage } = storageModular;
secondStorage = getStorage(firebase.app(), secondStorageBucket);
});

it('should write a file to the second storage bucket', async function () {
const { ref } = storageModular;

// "only-second-bucket" is not an allowable path on live project for either bucket
const storageReference = ref(secondStorage, 'only-second-bucket/ok.txt');

await storageReference.putString('Hello World');
});

it('should throw exception on path not allowed on second bucket security rules', async function () {
const { ref } = storageModular;

// "react-native-firebase-testing" is not an allowed on second bucket, only "ony-second-bucket"
const storageReference = ref(
secondStorage,
'react-native-firebase-testing/should-fail.txt',
);

try {
await storageReference.putString('Hello World');
return Promise.reject(new Error('Did not throw'));
} catch (error) {
error.code.should.equal('storage/unauthorized');
error.message.should.equal(
'[storage/unauthorized] User is not authorized to perform the desired action.',
);
return Promise.resolve();
}
});
});

describe('firebase v8 compatibility', function () {
describe('toString()', function () {
it('returns the correct bucket path to the file', function () {
Expand Down
8 changes: 7 additions & 1 deletion packages/storage/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.seed = async function seed(path) {
storage: {
rules: `rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /b/react-native-firebase-testing.appspot.com/o {
match /{document=**} {
allow read, write: if false;
}
Expand All @@ -35,6 +35,12 @@ exports.seed = async function seed(path) {
allow read, write: if true;
}
}
match /b/react-native-firebase-testing/o {
match /only-second-bucket/{document=**} {
allow read, write: if true;
}
}
}`,
host: getE2eEmulatorHost(),
port: 9199,
Expand Down
21 changes: 14 additions & 7 deletions packages/storage/ios/RNFBStorage/RNFBStorageModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,15 @@ - (void)invalidate {
RCT_EXPORT_METHOD(useEmulator
: (FIRApp *)firebaseApp
: (nonnull NSString *)host
: (NSInteger)port) {
: (NSInteger)port
: (NSString *)bucketUrl) {
emulatorHost = host;
emulatorPort = port;
if (!emulatorConfigs[firebaseApp.name]) {
[[FIRStorage storageForApp:firebaseApp] useEmulatorWithHost:host port:port];
emulatorConfigs[firebaseApp.name] = @YES;
NSString *key = [self createEmulatorKey:bucketUrl appName:firebaseApp.name];

if (!emulatorConfigs[key]) {
[[FIRStorage storageForApp:firebaseApp URL:bucketUrl] useEmulatorWithHost:host port:port];
emulatorConfigs[key] = @YES;
}
}

Expand Down Expand Up @@ -557,6 +560,10 @@ - (void)invalidate {
#pragma mark -
#pragma mark Firebase Storage Internals

- (NSString *)createEmulatorKey:(NSString *)bucketUrl appName:(NSString *)appName {
return [NSString stringWithFormat:@"%@-%@", appName, bucketUrl];
}

- (void)addUploadTaskObservers:(FIRStorageUploadTask *)uploadTask
appDisplayName:(NSString *)appDisplayName
taskId:(NSNumber *)taskId
Expand Down Expand Up @@ -675,11 +682,11 @@ - (FIRStorageReference *)getReferenceFromUrl:(NSString *)url app:(FIRApp *)fireb
storage = [FIRStorage storageForApp:firebaseApp URL:bucket];

NSLog(@"Setting emulator - host %@ port %ld", emulatorHost, (long)emulatorPort);
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil &&
!emulatorConfigs[firebaseApp.name]) {
NSString *key = [self createEmulatorKey:bucket appName:firebaseApp.name];
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil && !emulatorConfigs[key]) {
@try {
[storage useEmulatorWithHost:emulatorHost port:emulatorPort];
emulatorConfigs[firebaseApp.name] = @YES;
emulatorConfigs[key] = @YES;
} @catch (NSException *e) {
NSLog(@"WARNING: Unable to set the Firebase Storage emulator settings. These must be set "
@"before any usages of Firebase Storage. If you see this log after a hot "
Expand Down
2 changes: 1 addition & 1 deletion packages/storage/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class FirebaseStorageModule extends FirebaseModule {
}
this.emulatorHost = host;
this.emulatorPort = port;
this.native.useEmulator(_host, port);
this.native.useEmulator(_host, port, this._customUrlOrRegion);
return [_host, port]; // undocumented return, just used to unit test android host remapping
}
}
Expand Down
4 changes: 3 additions & 1 deletion tests/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ function loadTests(_) {
// as data from previous runs pollutes following runs until re-install the app. Clear it.
firebase.firestore().clearPersistence();
}
if (platformSupportedModules.includes('storage'))
if (platformSupportedModules.includes('storage')) {
firebase.storage().useEmulator('localhost', 9199);
firebase.app().storage('gs://react-native-firebase-testing').useEmulator('localhost', 9199);
}
});

afterEach(async function afterEachTest() {
Expand Down

0 comments on commit 66faa1d

Please sign in to comment.