Skip to content

Firebase App Auto Init - #164

Merged
avishalom merged 48 commits into
masterfrom
auto_init
Jan 10, 2018
Merged

Firebase App Auto Init#164
avishalom merged 48 commits into
masterfrom
auto_init

Conversation

@avishalom

@avishalomavishalom commented Dec 18, 2017

Copy link
Copy Markdown
Contributor
  • Adding the auto_init mechanism.
  • The env variable pointing to the config file is blanked by the config file when running the tests.

@hiranya911hiranya911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadCONTRIBUTING.md
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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of #163

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will drop

Comment threadsrc/firebase-app.ts Outdated
let contents;
try {
contents = fs.readFileSync(process.env[this.firebaseInternals_.CONFIG_FILE_VAR], 'utf8');
} catch (ignored) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will this return an error? Does it make sense to expose this to the developer?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throwing

Comment threadsrc/firebase-namespace.ts Outdated
* Internals of a FirebaseNamespace instance.
*/
export class FirebaseNamespaceInternals {
public CONFIG_FILE_VAR: string = 'FIREBASE_CONFIG';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a constant. Also lets rename to something like FIREBASE_CONFIG_ENV_VAR.

Comment threadtest/resources/firebase_config.json Outdated
@@ -0,0 +1,5 @@
{
"databaseURL": "https://hipster-chat.firebaseio.com",
"projectId": "hipster-chat",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use some other obviously fake project ID. Same for other parameters in these files. See mock.key.json for an example.

@hiranya911hiranya911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good. Some suggestions to clean up and improve. Also need to cover a couple more cases in testes.

Comment threadsrc/firebase-namespace.ts Outdated
let globalCertCreds: {[key: string]: CertCredential} = {};
let globalRefreshTokenCreds: {[key: string]: RefreshTokenCredential} = {};
let globalCertCreds: {[key: string]: CertCredential} = {};
let globalRefreshTokenCreds: {[key: string]: RefreshTokenCredential} = {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert these changes and keep the diff small.

Comment threadsrc/firebase-namespace.ts Outdated
*/
public initializeApp(options: FirebaseAppOptions, appName = DEFAULT_APP_NAME): FirebaseApp {
public initializeApp(
options: FirebaseAppOptions = { credential: new ApplicationDefaultCredential() },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()};
}
}

Comment threadsrc/firebase-app.ts Outdated
/**
* Parse the file pointed to by the FIREBASE_CONFIG_FILE_VAR, if it exists
*/
private useConfigEnvVar_(): FirebaseAppOptions {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets rename this to loadOptionsFromEnvironment(). Also this function doesn't return anything, so the signature should be updated.

Comment threadsrc/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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define this at module level, so it can be used in tests.

Comment threadsrc/firebase-app.ts Outdated
this.options_.storageBucket !== undefined) {
return;
}
if (process.env[FIREBASE_CONFIG_FILE_VAR]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return early if the variable is not set.

if (!process.env[FIREBASE_CONFIG_FILE_VAR]) {
return;
}

Comment threadtest/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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't --> are not

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not throw when the config environment variable is not set

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a test case when env variable is not set, and options are not explicitly specified. Should result in an error.

Comment threadtest/unit/firebase-app.spec.ts Outdated
expect(app.options.storageBucket).to.equal("hipster-chat.appspot.mock");
});

it('should complain about a non existant file', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should throw when the environment variable points to non existing file

Comment threadtest/unit/firebase-app.spec.ts Outdated
expect(app.options.storageBucket).to.equal(undefined);
});

it('should init with no params setting default credential', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add another test case where the environment variable is set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'?

@hiranya911hiranya911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. A few nits to address, and then I can lgtm

Comment threadsrc/firebase-app.ts Outdated
/**
* Constant holding the enviromnet variable that holds the default config.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant empty line

Comment threadsrc/firebase-app.ts Outdated
/**
* List of keys expected in the config file.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove

Comment threadsrc/firebase-app.ts Outdated
if (allSpecified) {
return;
}
if (process.env[FIREBASE_CONFIG_FILE_VAR] === undefined) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be OR'ed with the previous check for allSpecified

Comment threadsrc/firebase-app.ts Outdated
);
}
this.options_ = Object.assign(jsonContent, this.options_);
// for (let field of FIREBASE_CONFIG_KEYS) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove

Comment threadtest/unit/firebase-app.spec.ts Outdated
afterEach(() => {
this.clock.restore();

if (firebaseConfigVar == undefined || firebaseConfigVar == 'undefined') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check looks weird. Do we need it at all?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment threadtest/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' ;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file empty? I would recommend adding some obviously malformed content in there to make that obvious.

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'should use explicitly specified options when available'

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear what we are testing here. Please revise the description.

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'should init when no init arguments are provided'

Comment threadtest/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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need one more test case for when options are not specified, and neither the env variable is set.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is the previous case

@avishalomavishalom changed the title Auto initFirebase App Auto InitDec 23, 2017

@hiranya911hiranya911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good. A few minor nits, and then I can approve.

Comment threadsrc/firebase-app.ts Outdated

if (typeof this.options_ !== 'object' || this.options_ === null) {
// Ensure the options are a non-null object
// This shouldn't hapen

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove

Comment threadsrc/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' ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let filePath = process.env[FIREBASE_CONFIG_FILE_VAR];
if (!validator.isNonEmptyString(filePath)) {
return {};
}

Comment threadsrc/firebase-namespace.ts Outdated
return {};
}
let contents;
try {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add empty line

@@ -0,0 +1,4 @@
{
"databaseUrl": "https://hipster-chat.firebaseio.mock",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add another key that is obviously bad. Something like "notSupported": "some value"

Comment threadtest/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', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No options should be passed here -- I think.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is the following case.
(this case can be deleted, but no harm in leaving it in)

@avishalomavishalom assigned hiranya911 and unassigned avishalomJan 2, 2018
@avishalom

Copy link
Copy Markdown
ContributorAuthor

ok, done + added the json from env var option

@hiranya911hiranya911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a couple of nits. No further reviews necessary. Thanks for putting it all together.

Comment threadsrc/firebase-namespace.ts Outdated
if (!validator.isNonEmptyString(config)) {
return {};
}
let contents;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Move the declaration of contents into the try block.

Comment threadsrc/firebase-namespace.ts Outdated
// Assume filename.
contents = fs.readFileSync(config, 'utf8');
}
jsonContent = JSON.parse(contents);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Return here and remove the jsonContent variable: return JSON.parse(contents) as FirebaseAppOptions

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@avishalom@bojeil-google@hiranya911