Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch getDefaults() errors and do not throw #6686

Merged
merged 4 commits into from Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/grumpy-suits-pump.md
@@ -0,0 +1,5 @@
---
'@firebase/util': patch
---

Catch errors when the SDK checks for `__FIREBASE_DEFAULTS__` and do not block other app functionality.
31 changes: 26 additions & 5 deletions packages/util/src/defaults.ts
Expand Up @@ -70,7 +70,14 @@ const getDefaultsFromCookie = (): FirebaseDefaults | undefined => {
if (typeof document === 'undefined') {
return;
}
const match = document.cookie.match(/__FIREBASE_DEFAULTS__=([^;]+)/);
let match;
try {
match = document.cookie.match(/__FIREBASE_DEFAULTS__=([^;]+)/);
} catch (e) {
// Some environments such as Angular Universal SSR have a
// `document` object but error on accessing `document.cookie`.
return;
}
const decoded = match && base64Decode(match[1]);
return decoded && JSON.parse(decoded);
};
Expand All @@ -81,10 +88,24 @@ const getDefaultsFromCookie = (): FirebaseDefaults | undefined => {
* (2) if such an object was provided on a shell environment variable
* (3) if such an object exists in a cookie
*/
const getDefaults = (): FirebaseDefaults | undefined =>
getDefaultsFromGlobal() ||
getDefaultsFromEnvVariable() ||
getDefaultsFromCookie();
const getDefaults = (): FirebaseDefaults | undefined => {
try {
return (
getDefaultsFromGlobal() ||
getDefaultsFromEnvVariable() ||
getDefaultsFromCookie()
);
} catch (e) {
/**
* Catch-all for being unable to get __FIREBASE_DEFAULTS__ due
* to any environment case we have not accounted for. Log to
* info instead of swallowing so we can find these unknown cases
* and add paths for them if needed.
*/
console.info(`Unable to get __FIREBASE_DEFAULTS__ due to: ${e}`);
return;
}
};

/**
* Returns emulator host stored in the __FIREBASE_DEFAULTS__ object
Expand Down
67 changes: 57 additions & 10 deletions packages/util/test/defaults.test.ts
Expand Up @@ -14,16 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import { expect, use } from 'chai';
import { match, restore, SinonStub, stub } from 'sinon';
import sinonChai from 'sinon-chai';
import {
getDefaultEmulatorHost,
getDefaultEmulatorHostnameAndPort
} from '../src/defaults';
import { getGlobal } from '../src/environment';
import * as environment from '../src/environment';

use(sinonChai);

describe('getDefaultEmulatorHost', () => {
after(() => {
delete getGlobal().__FIREBASE_DEFAULTS__;
delete environment.getGlobal().__FIREBASE_DEFAULTS__;
});

context('with no config', () => {
Expand All @@ -32,9 +36,52 @@ describe('getDefaultEmulatorHost', () => {
});
});

context('with no config and process.env undefined', () => {
before(() => {
stub(process, 'env').value(undefined);
});
after(() => {
restore();
});
it('returns undefined', () => {
expect(getDefaultEmulatorHost('firestore')).to.be.undefined;
});
});

context('with no config and no document or document.cookie throws', () => {
before(() => {
// In Node tests document will not exist
if (typeof document !== 'undefined') {
stub(document, 'cookie').get(() => new Error('aaaah'));
}
});
after(() => {
restore();
});
it('returns undefined and calls console.info', () => {
expect(getDefaultEmulatorHost('firestore')).to.be.undefined;
});
});

context('with no config and something unexpected throws', () => {
let consoleInfoStub: SinonStub;
before(() => {
stub(environment, 'getGlobal').throws(new Error('getGlobal threw!'));
consoleInfoStub = stub(console, 'info');
});
after(() => {
delete process.env.__FIREBASE_DEFAULTS__;
restore();
});
it('returns undefined and calls console.info with the error', () => {
expect(getDefaultEmulatorHost('firestore')).to.be.undefined;
expect(consoleInfoStub).to.be.calledWith(match('getGlobal threw!'));
});
});

context('with global config not listing the emulator', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
/* no firestore */
database: '127.0.0.1:8080'
Expand All @@ -49,7 +96,7 @@ describe('getDefaultEmulatorHost', () => {

context('with IPv4 hostname in global config', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
firestore: '127.0.0.1:8080'
}
Expand All @@ -63,7 +110,7 @@ describe('getDefaultEmulatorHost', () => {

context('with quoted IPv6 hostname in global config', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
firestore: '[::1]:8080'
}
Expand All @@ -78,7 +125,7 @@ describe('getDefaultEmulatorHost', () => {

describe('getDefaultEmulatorHostnameAndPort', () => {
after(() => {
delete getGlobal().__FIREBASE_DEFAULTS__;
delete environment.getGlobal().__FIREBASE_DEFAULTS__;
});

context('with no config', () => {
Expand All @@ -89,7 +136,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => {

context('with global config not listing the emulator', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
/* no firestore */
database: '127.0.0.1:8080'
Expand All @@ -104,7 +151,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => {

context('with IPv4 hostname in global config', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
firestore: '127.0.0.1:8080'
}
Expand All @@ -121,7 +168,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => {

context('with quoted IPv6 hostname in global config', () => {
before(() => {
getGlobal().__FIREBASE_DEFAULTS__ = {
environment.getGlobal().__FIREBASE_DEFAULTS__ = {
emulatorHosts: {
firestore: '[::1]:8080'
}
Expand Down