[go: nahoru, domu]

Skip to content

Commit

Permalink
Static Loading of Service Namespaces (firebase#106)
Browse files Browse the repository at this point in the history
* Getting rid of the monkeypatching code

* Implemented static on-demand loading for all services except RTDB

* Using Object.assign() instead of _.assign()
  • Loading branch information
hiranya911 committed Oct 17, 2017
1 parent 4cb5104 commit c28ac52
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 315 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@
"@types/node": "^8.0.32",
"faye-websocket": "0.9.3",
"jsonwebtoken": "7.1.9",
"node-forge": "0.7.1",
"lodash": "^4.6.1"
"node-forge": "0.7.1"
},
"devDependencies": {
"@types/chai": "^3.4.34",
Expand All @@ -84,6 +83,7 @@
"gulp-replace": "^0.5.4",
"gulp-tslint": "^6.0.2",
"gulp-typescript": "^3.1.2",
"lodash": "^4.6.1",
"merge2": "^1.0.2",
"mocha": "^3.5.0",
"nock": "^8.0.0",
Expand Down
9 changes: 2 additions & 7 deletions src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import * as validator from '../utils/validator';
/**
* Internals of an Auth instance.
*/
export class AuthInternals implements FirebaseServiceInternalsInterface {
class AuthInternals implements FirebaseServiceInternalsInterface {
/**
* Deletes the service and its associated resources.
*
Expand All @@ -51,7 +51,7 @@ export interface ListUsersResult {
/**
* Auth service bound to the provided app.
*/
class Auth implements FirebaseServiceInterface {
export class Auth implements FirebaseServiceInterface {
public INTERNAL: AuthInternals = new AuthInternals();

private app_: FirebaseApp;
Expand Down Expand Up @@ -286,8 +286,3 @@ class Auth implements FirebaseServiceInterface {
});
};
};


export {
Auth,
}
56 changes: 0 additions & 56 deletions src/auth/register-auth.ts

This file was deleted.

74 changes: 47 additions & 27 deletions src/firebase-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import {FirebaseServiceInterface} from './firebase-service';
import {FirebaseNamespaceInternals} from './firebase-namespace';
import {AppErrorCodes, FirebaseAppError} from './utils/error';
import {Firestore} from '@google-cloud/firestore';
import {FirestoreService} from './firestore/firestore';

import {Auth} from './auth/auth';
import {Messaging} from './messaging/messaging';
import {Storage} from './storage/storage';

/**
* Type representing a callback which is called every time an app lifecycle event occurs.
Expand Down Expand Up @@ -276,16 +280,14 @@ export class FirebaseApp {
}

/**
* Firebase services available off of a FirebaseApp instance. These are monkey-patched via
* registerService(), but we need to include a dummy implementation to get TypeScript to
* compile it without errors.
* Returns the Auth service instance associated with this app.
*
* @return {Auth} The Auth service instance of this app.
*/
/* istanbul ignore next */
public auth(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase auth() service has not been registered.',
);
public auth(): Auth {
return this.ensureService_('auth', () => {
return new Auth(this);
});
}

/* istanbul ignore next */
Expand All @@ -296,28 +298,33 @@ export class FirebaseApp {
);
}

/* istanbul ignore next */
public messaging(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase messaging() service has not been registered.',
);
/**
* Returns the Messaging service instance associated with this app.
*
* @return {Messaging} The Messaging service instance of this app.
*/
public messaging(): Messaging {
return this.ensureService_('messaging', () => {
return new Messaging(this);
});
}

/* istanbul ignore next */
public storage(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase storage() service has not been registered.',
);
/**
* Returns the Storage service instance associated with this app.
*
* @return {Storage} The Storage service instance of this app.
*/
public storage(): Storage {
return this.ensureService_('storage', () => {
return new Storage(this);
});
}

/* istanbul ignore next */
public firestore(): Firestore {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase firestore() service has not been registered.',
);
let service: FirestoreService = this.ensureService_('firestore', () => {
return new FirestoreService(this);
});
return service.client;
}

/**
Expand Down Expand Up @@ -359,9 +366,22 @@ export class FirebaseApp {
});
}

private ensureService_<T extends FirebaseServiceInterface>(serviceName: string, initializer: () => T): T {
this.checkDestroyed_();

let service: T;
if (serviceName in this.services_) {
service = this.services_[serviceName] as T;
} else {
service = initializer();
this.services_[serviceName] = service;
}
return service;
}

/**
* Returns the service instance associated with this FirebaseApp instance (creating it on demand
* if needed).
* if needed). This is used for looking up monkeypatched service instances.
*
* @param {string} serviceName The name of the service instance to return.
* @return {FirebaseServiceInterface} The service instance with the provided name.
Expand Down
79 changes: 51 additions & 28 deletions src/firebase-namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ import {
} from './auth/credential';
import {Firestore} from '@google-cloud/firestore';

import {Auth} from './auth/auth';
import {Messaging} from './messaging/messaging';
import {Storage} from './storage/storage';

const DEFAULT_APP_NAME = '[DEFAULT]';

let globalAppDefaultCred: ApplicationDefaultCredential;
let globalCertCreds: { [key: string]: CertCredential } = {};
let globalRefreshTokenCreds: { [key: string]: RefreshTokenCredential } = {};


export interface FirebaseServiceNamespace <T extends FirebaseServiceInterface> {
export interface FirebaseServiceNamespace <T> {
(app?: FirebaseApp): T;
[key: string]: any;
}


Expand Down Expand Up @@ -273,16 +278,15 @@ export class FirebaseNamespace {
}

/**
* Firebase services available off of a FirebaseNamespace instance. These are monkey-patched via
* registerService(), but we need to include a dummy implementation to get TypeScript to
* compile it without errors.
* Gets the `Auth` service namespace. The returned namespace can be used to get the
* `Auth` service for the default app or an explicitly specified app.
*/
/* istanbul ignore next */
public auth(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase auth() service has not been registered.',
);
get auth(): FirebaseServiceNamespace<Auth> {
const ns: FirebaseNamespace = this;
let fn: FirebaseServiceNamespace<Auth> = (app?: FirebaseApp) => {
return ns.ensureApp(app).auth();
};
return Object.assign(fn, {Auth});
}

/* istanbul ignore next */
Expand All @@ -293,28 +297,40 @@ export class FirebaseNamespace {
);
}

/* istanbul ignore next */
public messaging(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase messaging() service has not been registered.',
);
/**
* Gets the `Messaging` service namespace. The returned namespace can be used to get the
* `Messaging` service for the default app or an explicitly specified app.
*/
get messaging(): FirebaseServiceNamespace<Messaging> {
const ns: FirebaseNamespace = this;
let fn: FirebaseServiceNamespace<Messaging> = (app?: FirebaseApp) => {
return ns.ensureApp(app).messaging();
};
return Object.assign(fn, {Messaging});
}

/* istanbul ignore next */
public storage(): FirebaseServiceInterface {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase storage() service has not been registered.',
);
/**
* Gets the `Storage` service namespace. The returned namespace can be used to get the
* `Storage` service for the default app or an explicitly specified app.
*/
get storage(): FirebaseServiceNamespace<Storage> {
const ns: FirebaseNamespace = this;
let fn: FirebaseServiceNamespace<Storage> = (app?: FirebaseApp) => {
return ns.ensureApp(app).storage();
};
return Object.assign(fn, {Storage});
}

/* istanbul ignore next */
public firestore(): Firestore {
throw new FirebaseAppError(
AppErrorCodes.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Firebase firestore() service has not been registered.',
);
/**
* Gets the `Firestore` service namespace. The returned namespace can be used to get the
* `Firestore` service for the default app or an explicitly specified app.
*/
get firestore(): FirebaseServiceNamespace<Firestore> {
const ns: FirebaseNamespace = this;
let fn: FirebaseServiceNamespace<Firestore> = (app?: FirebaseApp) => {
return ns.ensureApp(app).firestore();
};
return Object.assign(fn, require('@google-cloud/firestore'));
}

/**
Expand Down Expand Up @@ -348,4 +364,11 @@ export class FirebaseNamespace {
public get apps(): FirebaseApp[] {
return this.INTERNAL.apps;
}

private ensureApp(app?: FirebaseApp): FirebaseApp {
if (typeof app === 'undefined') {
app = this.app();
}
return app;
}
}
40 changes: 25 additions & 15 deletions src/firestore/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ class FirestoreInternals implements FirebaseServiceInternalsInterface {
}
}

export class FirestoreService implements FirebaseServiceInterface {
public INTERNAL: FirestoreInternals = new FirestoreInternals();

private appInternal: FirebaseApp;
private firestoreClient: Firestore;

constructor(app: FirebaseApp) {
this.firestoreClient = initFirestore(app);
this.appInternal = app;
}

/**
* Returns the app associated with this Storage instance.
*
* @return {FirebaseApp} The app associated with this Storage instance.
*/
get app(): FirebaseApp {
return this.appInternal;
}

get client(): Firestore {
return this.firestoreClient;
}
}

function initFirestore(app: FirebaseApp): Firestore {
if (!validator.isNonNullObject(app) || !('options' in app)) {
throw new FirebaseFirestoreError({
Expand Down Expand Up @@ -88,18 +113,3 @@ function initFirestore(app: FirebaseApp): Firestore {
}
return new Firestore(options);
}

/**
* Creates a new Firestore service instance for the given FirebaseApp.
*
* @param {FirebaseApp} app The App for this Firestore service.
* @return {FirebaseServiceInterface} A Firestore service instance.
*/
export function initFirestoreService(app: FirebaseApp): FirebaseServiceInterface {
let firestore: any = initFirestore(app);

// Extend the Firestore client object so it implements FirebaseServiceInterface.
utils.addReadonlyGetter(firestore, 'app', app);
firestore.INTERNAL = new FirestoreInternals();
return firestore;
}
Loading

0 comments on commit c28ac52

Please sign in to comment.