Conversation
hiranya911
left a comment
There was a problem hiding this comment.
This is a start. But it seems to be missing some of the key elements. For instance I don't see the changes to initializeApp(), which makes it possible to call it without any arguments. I also think the logic for parsing the JSON file should be inside or close to initializeApp(), as opposed to the constructor of FirebaseApp.
| account key file from the "Settings > Service Accounts" page of the project, and copy it to | ||
| `test/resources/key.json`. Also obtain the API key for the same project from "Settings > General", | ||
| and save it to `test/resource/apikey.txt`. Finally, to run the integration test suite: | ||
| and save it to `test/resources/apikey.txt`. Finally, to run the integration test suite: |
src/firebase-app.ts
Outdated
| let contents; | ||
| try { | ||
| contents = fs.readFileSync(process.env[this.firebaseInternals_.CONFIG_FILE_VAR], 'utf8'); | ||
| } catch (ignored) { |
There was a problem hiding this comment.
When will this return an error? Does it make sense to expose this to the developer?
src/firebase-namespace.ts
Outdated
| * Internals of a FirebaseNamespace instance. | ||
| */ | ||
| export class FirebaseNamespaceInternals { | ||
| public CONFIG_FILE_VAR: string = 'FIREBASE_CONFIG'; |
There was a problem hiding this comment.
Please make this a constant. Also lets rename to something like FIREBASE_CONFIG_ENV_VAR.
test/resources/firebase_config.json
Outdated
| @@ -0,0 +1,5 @@ | |||
| { | |||
| "databaseURL": "https://hipster-chat.firebaseio.com", | |||
| "projectId": "hipster-chat", | |||
There was a problem hiding this comment.
Please use some other obviously fake project ID. Same for other parameters in these files. See mock.key.json for an example.
hiranya911
left a comment
There was a problem hiding this comment.
Looks mostly good. Some suggestions to clean up and improve. Also need to cover a couple more cases in testes.
src/firebase-namespace.ts
Outdated
| let globalCertCreds: { [key: string]: CertCredential } = {}; | ||
| let globalRefreshTokenCreds: { [key: string]: RefreshTokenCredential } = {}; | ||
| let globalCertCreds: {[key: string]: CertCredential} = {}; | ||
| let globalRefreshTokenCreds: {[key: string]: RefreshTokenCredential} = {}; |
There was a problem hiding this comment.
Please revert these changes and keep the diff small.
src/firebase-namespace.ts
Outdated
| */ | ||
| public initializeApp(options: FirebaseAppOptions, appName = DEFAULT_APP_NAME): FirebaseApp { | ||
| public initializeApp( | ||
| options: FirebaseAppOptions = { credential: new ApplicationDefaultCredential() }, |
There was a problem hiding this comment.
it is a little weird to be using an entire object as a default argument. Will the following make this a bit more readable?
public initializeApp(options?: FirebaseAppOptions, appName?: string): FirebaseApp {
if (typeof options === 'undefined') {
options = {credential: new ApplicationDefaultCredential()};
}
}
src/firebase-app.ts
Outdated
| /** | ||
| * Parse the file pointed to by the FIREBASE_CONFIG_FILE_VAR, if it exists | ||
| */ | ||
| private useConfigEnvVar_(): FirebaseAppOptions { |
There was a problem hiding this comment.
Lets rename this to loadOptionsFromEnvironment(). Also this function doesn't return anything, so the signature should be updated.
src/firebase-app.ts
Outdated
| * Parse the file pointed to by the FIREBASE_CONFIG_FILE_VAR, if it exists | ||
| */ | ||
| private useConfigEnvVar_(): FirebaseAppOptions { | ||
| const FIREBASE_CONFIG_FILE_VAR: string = 'FIREBASE_CONFIG'; |
There was a problem hiding this comment.
Define this at module level, so it can be used in tests.
src/firebase-app.ts
Outdated
| this.options_.storageBucket !== undefined) { | ||
| return; | ||
| } | ||
| if (process.env[FIREBASE_CONFIG_FILE_VAR]) { |
There was a problem hiding this comment.
Return early if the variable is not set.
if (!process.env[FIREBASE_CONFIG_FILE_VAR]) {
return;
}
test/unit/firebase-app.spec.ts
Outdated
| }); | ||
|
|
||
| it('should overwrite the config values with the ones in the config file', () => { | ||
| process.env[FIREBASE_CONFIG_FILE_VAR] = './test/resources/firebase_config.json'; |
There was a problem hiding this comment.
You should first unset this variable in the beforeEach hook, and restore it to the original value in afterEach hook. Then you don't have to delete it in the test cases. See Firestore unit tests for some examples.
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal('bucketName.appspot.com'); | ||
| }); | ||
|
|
||
| it('should use the existing values when some of them aren\'t overwritten', () => { |
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal(undefined); | ||
| }); | ||
|
|
||
| it('should init without problem when the config env var isn\'t set', () => { |
There was a problem hiding this comment.
should not throw when the config environment variable is not set
There was a problem hiding this comment.
Also add a test case when env variable is not set, and options are not explicitly specified. Should result in an error.
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal("hipster-chat.appspot.mock"); | ||
| }); | ||
|
|
||
| it('should complain about a non existant file', () => { |
There was a problem hiding this comment.
should throw when the environment variable points to non existing file
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal(undefined); | ||
| }); | ||
|
|
||
| it('should init with no params setting default credential', () => { |
There was a problem hiding this comment.
Add another test case where the environment variable is set.
There was a problem hiding this comment.
Ok, it wasn't clear what's going on here. Shall we update the description to something like 'should init with application default credentials when no options provided and the environment variable is not set'?
hiranya911
left a comment
There was a problem hiding this comment.
Looks good. A few nits to address, and then I can lgtm
src/firebase-app.ts
Outdated
| /** | ||
| * Constant holding the enviromnet variable that holds the default config. | ||
| */ | ||
|
|
src/firebase-app.ts
Outdated
| /** | ||
| * List of keys expected in the config file. | ||
| */ | ||
|
|
src/firebase-app.ts
Outdated
| if (allSpecified) { | ||
| return; | ||
| } | ||
| if (process.env[FIREBASE_CONFIG_FILE_VAR] === undefined) { |
There was a problem hiding this comment.
This can be OR'ed with the previous check for allSpecified
src/firebase-app.ts
Outdated
| ); | ||
| } | ||
| this.options_ = Object.assign(jsonContent, this.options_); | ||
| // for (let field of FIREBASE_CONFIG_KEYS) { |
test/unit/firebase-app.spec.ts
Outdated
| afterEach(() => { | ||
| this.clock.restore(); | ||
|
|
||
| if (firebaseConfigVar == undefined || firebaseConfigVar == 'undefined') { |
There was a problem hiding this comment.
This check looks weird. Do we need it at all?
There was a problem hiding this comment.
setting the process.env[FIREBASE_CONFIG_FILE_VAR] to the value of firebaseConfigVar : string when that is undefined results in the assignment of the string 'undefined'
the second check is moot though
test/unit/firebase-app.spec.ts
Outdated
| }); | ||
|
|
||
| it('should throw when the environment variable points to non parsable file', () => { | ||
| process.env[FIREBASE_CONFIG_FILE_VAR] = './test/resources/firebase_config_bad.json' ; |
There was a problem hiding this comment.
Is this file empty? I would recommend adding some obviously malformed content in there to make that obvious.
test/unit/firebase-app.spec.ts
Outdated
| }).to.throw(`Failed to parse app options file`); | ||
| }); | ||
|
|
||
| it('should use the existing values when some of them exist', () => { |
There was a problem hiding this comment.
'should use explicitly specified options when available'
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal('bucketName.appspot.com'); | ||
| }); | ||
|
|
||
| it('should use the existing values when some of them are not overwritten', () => { |
There was a problem hiding this comment.
Not clear what we are testing here. Please revise the description.
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal(undefined); | ||
| }); | ||
|
|
||
| it('should init with params from the config file, setting default credential', () => { |
There was a problem hiding this comment.
'should init when no init arguments are provided'
test/unit/firebase-app.spec.ts
Outdated
| }); | ||
|
|
||
| it('should init with params from the config file, setting default credential', () => { | ||
| process.env[FIREBASE_CONFIG_FILE_VAR] = './test/resources/firebase_config.json'; |
There was a problem hiding this comment.
Need one more test case for when options are not specified, and neither the env variable is set.
There was a problem hiding this comment.
that is the previous case
hiranya911
left a comment
There was a problem hiding this comment.
Looks mostly good. A few minor nits, and then I can approve.
src/firebase-app.ts
Outdated
|
|
||
| if (typeof this.options_ !== 'object' || this.options_ === null) { | ||
| // Ensure the options are a non-null object | ||
| // This shouldn't hapen |
src/firebase-namespace.ts
Outdated
| * Parse the file pointed to by the FIREBASE_CONFIG_FILE_VAR, if it exists | ||
| */ | ||
| private loadOptionsFromEnvVar(): FirebaseAppOptions { | ||
| if (typeof process.env[FIREBASE_CONFIG_FILE_VAR] === 'undefined' || |
There was a problem hiding this comment.
let filePath = process.env[FIREBASE_CONFIG_FILE_VAR];
if (!validator.isNonEmptyString(filePath)) {
return {};
}
src/firebase-namespace.ts
Outdated
| return {}; | ||
| } | ||
| let contents; | ||
| try { |
There was a problem hiding this comment.
Merge these try-catch blocks into one:
try {
let contents = fs.readFileSync(filePath, 'utf8');
return JSON.parse(contents) as FirebaseAppOptions;
} catch (error) {
// error message: 'Failed to load app options from environment: ' + error
}
| * @return {FirebaseApp} A new FirebaseApp instance. | ||
| */ | ||
| public initializeApp(options: FirebaseAppOptions, appName?: string): FirebaseApp { | ||
| public initializeApp(options?: FirebaseAppOptions, appName?: string): FirebaseApp { |
There was a problem hiding this comment.
Update the argument description for options.
| expect(app.options.databaseURL).to.undefined; | ||
| expect(app.options.projectId).to.be.undefined; | ||
| expect(app.options.storageBucket).to.undefined; | ||
| }); |
| @@ -0,0 +1,4 @@ | |||
| { | |||
| "databaseUrl": "https://hipster-chat.firebaseio.mock", | |||
There was a problem hiding this comment.
Please add another key that is obviously bad. Something like "notSupported": "some value"
test/unit/firebase-app.spec.ts
Outdated
| expect(app.options.storageBucket).to.equal('bucketName.appspot.com'); | ||
| }); | ||
|
|
||
| it('should ignore the config file even if some fields are missing', () => { |
There was a problem hiding this comment.
should not throw if some...
| }); | ||
|
|
||
| it('should not throw when the config environment variable is not set', () => { | ||
| const app = firebaseNamespace.initializeApp(mocks.appOptionsNoDatabaseUrl, mocks.appName); |
There was a problem hiding this comment.
No options should be passed here -- I think.
There was a problem hiding this comment.
that is the following case.
(this case can be deleted, but no harm in leaving it in)
|
ok, done + added the json from env var option |
hiranya911
left a comment
There was a problem hiding this comment.
LGTM with a couple of nits. No further reviews necessary. Thanks for putting it all together.
src/firebase-namespace.ts
Outdated
| if (!validator.isNonEmptyString(config)) { | ||
| return {}; | ||
| } | ||
| let contents; |
There was a problem hiding this comment.
Nit: Move the declaration of contents into the try block.
src/firebase-namespace.ts
Outdated
| // Assume filename. | ||
| contents = fs.readFileSync(config, 'utf8'); | ||
| } | ||
| jsonContent = JSON.parse(contents); |
There was a problem hiding this comment.
Nit: Return here and remove the jsonContent variable: return JSON.parse(contents) as FirebaseAppOptions
Uh oh!
There was an error while loading. Please reload this page.