diff --git a/karma.conf.js b/karma.conf.js index 8aae5da5b..4c1d6c748 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -30,6 +30,7 @@ module.exports = function(config) { 'dist/packages-dist/bundles/auth.umd.{js,map}', 'dist/packages-dist/bundles/database.umd.{js,map}', 'dist/packages-dist/bundles/firestore.umd.{js,map}', + 'dist/packages-dist/bundles/functions.umd.{js,map}', 'dist/packages-dist/bundles/storage.umd.{js,map}', 'dist/packages-dist/bundles/database-deprecated.umd.{js,map}', 'dist/packages-dist/bundles/test.umd.{js,map}', diff --git a/package.json b/package.json index 2a5cea406..ad78f7fb4 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@firebase/database-types": "^0.3.1", "@firebase/firestore": "^0.5.2", "@firebase/firestore-types": "^0.4.1", + "@firebase/functions": "^0.2.2", + "@firebase/functions-types": "^0.1.2", "@firebase/messaging-types": "^0.2.2", "@firebase/storage": "^0.2.2", "@firebase/storage-types": "^0.2.2", diff --git a/src/core/firebase.app.module.ts b/src/core/firebase.app.module.ts index 2373419bb..265fe0d88 100644 --- a/src/core/firebase.app.module.ts +++ b/src/core/firebase.app.module.ts @@ -9,6 +9,7 @@ import { FirebaseDatabase } from '@firebase/database-types'; import { FirebaseMessaging } from '@firebase/messaging-types'; import { FirebaseStorage } from '@firebase/storage-types'; import { FirebaseFirestore } from '@firebase/firestore-types'; +import { FirebaseFunctions } from '@firebase/functions-types'; export class FirebaseApp implements _FirebaseApp { name: string; @@ -20,6 +21,7 @@ export class FirebaseApp implements _FirebaseApp { storage: (storageBucket?: string) => FirebaseStorage; delete: () => Promise; firestore: () => FirebaseFirestore; + functions: () => FirebaseFunctions; } export function _firebaseAppFactory(options: FirebaseOptions, nameOrConfig?: string | FirebaseAppConfig) { diff --git a/src/functions/functions.module.ts b/src/functions/functions.module.ts new file mode 100644 index 000000000..0809e29f6 --- /dev/null +++ b/src/functions/functions.module.ts @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; +import { AngularFireFunctions } from './functions'; +import '@firebase/functions' + +@NgModule({ + providers: [ AngularFireFunctions ] +}) +export class AngularFireFunctionsModule { } diff --git a/src/functions/functions.spec.ts b/src/functions/functions.spec.ts new file mode 100644 index 000000000..ec0a8109e --- /dev/null +++ b/src/functions/functions.spec.ts @@ -0,0 +1,75 @@ +import { ReflectiveInjector, Provider } from '@angular/core'; +import { TestBed, inject } from '@angular/core/testing'; +import { FirebaseApp, FirebaseOptionsToken, AngularFireModule, FirebaseNameOrConfigToken } from 'angularfire2'; +import { AngularFireFunctions, AngularFireFunctionsModule } from 'angularfire2/functions'; +import { COMMON_CONFIG } from './test-config'; + +describe('AngularFireFunctions', () => { + let app: FirebaseApp; + let afFns: AngularFireFunctions; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + AngularFireModule.initializeApp(COMMON_CONFIG), + AngularFireFunctionsModule + ] + }); + inject([FirebaseApp, AngularFireFunctions], (app_: FirebaseApp, _fn: AngularFireFunctions) => { + app = app_; + afFns = _fn; + })(); + }); + + afterEach(done => { + app.delete(); + done(); + }); + + it('should be exist', () => { + expect(afFns instanceof AngularFireFunctions).toBe(true); + }); + + it('should have the Firebase Functions instance', () => { + expect(afFns.functions).toBeDefined(); + }); + +}); + +const FIREBASE_APP_NAME_TOO = (Math.random() + 1).toString(36).substring(7); + +describe('AngularFireFunctions with different app', () => { + let app: FirebaseApp; + let afFns: AngularFireFunctions; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + AngularFireModule.initializeApp(COMMON_CONFIG), + AngularFireFunctionsModule + ], + providers: [ + { provide: FirebaseNameOrConfigToken, useValue: FIREBASE_APP_NAME_TOO }, + { provide: FirebaseOptionsToken, useValue: COMMON_CONFIG } + ] + }); + inject([FirebaseApp, AngularFireFunctions], (app_: FirebaseApp, _fns: AngularFireFunctions) => { + app = app_; + afFns = _fns; + })(); + }); + + afterEach(done => { + app.delete(); + done(); + }); + + describe('', () => { + + it('should be an AngularFireAuth type', () => { + expect(afFns instanceof AngularFireFunctions).toEqual(true); + }); + + }); + +}); diff --git a/src/functions/functions.ts b/src/functions/functions.ts new file mode 100644 index 000000000..41bc0dda8 --- /dev/null +++ b/src/functions/functions.ts @@ -0,0 +1,46 @@ +import { FirebaseFunctions } from '@firebase/functions-types'; +import { FirebaseOptions, FirebaseAppConfig } from '@firebase/app-types'; +import { Injectable, Inject, Optional, NgZone, PLATFORM_ID } from '@angular/core'; +import { Observable, from } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { FirebaseOptionsToken, FirebaseNameOrConfigToken, _firebaseAppFactory, FirebaseZoneScheduler } from 'angularfire2'; + +@Injectable() +export class AngularFireFunctions { + + /** + * Firebase Functions instance + */ + public readonly functions: FirebaseFunctions; + + public readonly scheduler: FirebaseZoneScheduler; + + constructor( + @Inject(FirebaseOptionsToken) options:FirebaseOptions, + @Optional() @Inject(FirebaseNameOrConfigToken) nameOrConfig:string|FirebaseAppConfig|undefined, + @Inject(PLATFORM_ID) platformId: Object, + zone: NgZone + ) { + this.scheduler = new FirebaseZoneScheduler(zone, platformId); + + this.functions = zone.runOutsideAngular(() => { + const app = _firebaseAppFactory(options, nameOrConfig); + return app.functions(); + }); + + } + + public httpsCallable(name: string) { + const callable = this.functions.httpsCallable(name); + return (data: T) => { + const callable$ = from(callable(data)); + return this.scheduler.runOutsideAngular( + callable$.pipe( + map(r => r.data as R) + ) + ) + } + } + +} diff --git a/src/functions/index.spec.ts b/src/functions/index.spec.ts new file mode 100644 index 000000000..64df0015c --- /dev/null +++ b/src/functions/index.spec.ts @@ -0,0 +1 @@ +import './functions.spec'; diff --git a/src/functions/index.ts b/src/functions/index.ts new file mode 100644 index 000000000..4aaf8f92e --- /dev/null +++ b/src/functions/index.ts @@ -0,0 +1 @@ +export * from './public_api'; diff --git a/src/functions/package.json b/src/functions/package.json new file mode 100644 index 000000000..62b3386ae --- /dev/null +++ b/src/functions/package.json @@ -0,0 +1,31 @@ +{ + "name": "angularfire2/functions", + "version": "ANGULARFIRE2_VERSION", + "description": "The functions module", + "main": "../bundles/functions.umd.js", + "module": "index.js", + "es2015": "./es2015/index.js", + "keywords": [ + "angular", + "firebase", + "rxjs" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/angular/angularfire2.git" + }, + "author": "angular,firebase", + "license": "MIT", + "peerDependencies": { + "angularfire2": "ANGULARFIRE2_VERSION", + "@angular/common": "ANGULAR_VERSION", + "@angular/core": "ANGULAR_VERSION", + "@angular/platform-browser": "ANGULAR_VERSION", + "@angular/platform-browser-dynamic": "ANGULAR_VERSION", + "@firebase/app": "FIREBASE_APP_VERSION", + "@firebase/functions": "FIREBASE_FUNCTIONS_VERSION", + "rxjs": "RXJS_VERSION", + "zone.js": "ZONEJS_VERSION" + }, + "typings": "index.d.ts" +} diff --git a/src/functions/public_api.ts b/src/functions/public_api.ts new file mode 100644 index 000000000..ebea7ec53 --- /dev/null +++ b/src/functions/public_api.ts @@ -0,0 +1,2 @@ +export * from './functions'; +export * from './functions.module'; diff --git a/src/functions/test-config.ts b/src/functions/test-config.ts new file mode 100644 index 000000000..4b69c98dd --- /dev/null +++ b/src/functions/test-config.ts @@ -0,0 +1,7 @@ + +export const COMMON_CONFIG = { + apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA", + authDomain: "angularfire2-test.firebaseapp.com", + databaseURL: "https://angularfire2-test.firebaseio.com", + storageBucket: "angularfire2-test.appspot.com", +}; diff --git a/src/functions/tsconfig-build.json b/src/functions/tsconfig-build.json new file mode 100644 index 000000000..97c12201b --- /dev/null +++ b/src/functions/tsconfig-build.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "module": "es2015", + "target": "es2015", + "noImplicitAny": false, + "outDir": "../../dist/packages-dist/functions/es2015", + "rootDir": ".", + "sourceMap": true, + "inlineSources": true, + "declaration": false, + "removeComments": true, + "strictNullChecks": true, + "lib": ["es2015", "dom", "es2015.promise", "es2015.collection", "es2015.iterable"], + "skipLibCheck": true, + "moduleResolution": "node", + "paths": { + "angularfire2": ["../../dist/packages-dist"] + } + }, + "files": [ + "index.ts", + "../../node_modules/zone.js/dist/zone.js.d.ts" + ], + "angularCompilerOptions": { + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "enableSummariesForJit": false + } +} + diff --git a/src/functions/tsconfig-esm.json b/src/functions/tsconfig-esm.json new file mode 100644 index 000000000..c5d589d43 --- /dev/null +++ b/src/functions/tsconfig-esm.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig-build.json", + "compilerOptions": { + "target": "es5", + "outDir": "../../dist/packages-dist/functions", + "declaration": true + }, + "files": [ + "public_api.ts", + "../../node_modules/zone.js/dist/zone.js.d.ts" + ], + "angularCompilerOptions": { + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "enableSummariesForJit": false, + "flatModuleOutFile": "index.js", + "flatModuleId": "angularfire2/functions" + } +} diff --git a/src/functions/tsconfig-test.json b/src/functions/tsconfig-test.json new file mode 100644 index 000000000..2f96c7bb9 --- /dev/null +++ b/src/functions/tsconfig-test.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig-esm.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "angularfire2": ["../../dist/packages-dist"], + "angularfire2/functions": ["../../dist/packages-dist/functions"] + } + }, + "files": [ + "index.spec.ts", + "../../node_modules/zone.js/dist/zone.js.d.ts" + ] +} diff --git a/src/root.spec.js b/src/root.spec.js index b60264afb..476e302b2 100644 --- a/src/root.spec.js +++ b/src/root.spec.js @@ -4,6 +4,7 @@ export * from './packages-dist/auth/auth.spec'; export * from './packages-dist/firestore/firestore.spec'; export * from './packages-dist/firestore/document/document.spec'; export * from './packages-dist/firestore/collection/collection.spec'; +export * from './packages-dist/functions/functions.spec'; export * from './packages-dist/database/database.spec'; export * from './packages-dist/database/utils.spec'; export * from './packages-dist/database/observable/fromRef.spec'; diff --git a/src/tsconfig.json b/src/tsconfig.json index 2a0a351e4..35fa29c65 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -14,6 +14,7 @@ "angularfire2/auth": ["./auth"], "angularfire2/database": ["./database"], "angularfire2/firestore": ["./firestore"], + "angularfire2/functions": ["./functions"], "angularfire2/storage": ["./storage"], "angularfire2/database-deprecated": ["./database-deprecated"] }, diff --git a/tools/build.js b/tools/build.js index e377cdeb2..9d77b0e62 100644 --- a/tools/build.js +++ b/tools/build.js @@ -21,12 +21,14 @@ const GLOBALS = { '@firebase/auth': 'firebase', '@firebase/database': 'firebase', '@firebase/firestore': 'firebase', + '@firebase/functions': 'firebase', '@firebase/storage': 'firebase', 'angularfire2': 'angularfire2', 'angularfire2/auth': 'angularfire2.auth', 'angularfire2/database': 'angularfire2.database', 'angularfire2/database-deprecated': 'angularfire2.database_deprecated', 'angularfire2/firestore': 'angularfire2.firestore', + 'angularfire2/functions': 'angularfire2.functions', 'angularfire2/storage': 'angularfire2.storage', 'zone.js': 'Zone' }; @@ -35,9 +37,10 @@ const GLOBALS = { const VERSIONS = { ANGULAR_VERSION: pkg.dependencies['@angular/core'], FIREBASE_APP_VERSION: pkg.dependencies['@firebase/app'], + FIREBASE_AUTH_VERSION: pkg.dependencies['@firebase/auth'], FIREBASE_DATABASE_VERSION: pkg.dependencies['@firebase/database'], FIREBASE_FIRESTORE_VERSION: pkg.dependencies['@firebase/firestore'], - FIREBASE_AUTH_VERSION: pkg.dependencies['@firebase/auth'], + FIREBASE_FUNCTIONS_VERSION: pkg.dependencies['@firebase/functions'], FIREBASE_STORAGE_VERSION: pkg.dependencies['@firebase/storage'], RXJS_VERSION: pkg.dependencies['rxjs'], ZONEJS_VERSION: pkg.dependencies['zone.js'], @@ -55,6 +58,7 @@ const MODULE_NAMES = { database: 'angularfire2.database', "database-deprecated": 'angularfire2.database_deprecated', firestore: 'angularfire2.firestore', + functions: 'angularfire2.functions', storage: 'angularfire2.storage' }; @@ -64,6 +68,7 @@ const ENTRIES = { database: `${process.cwd()}/dist/packages-dist/database/index.js`, "database-deprecated": `${process.cwd()}/dist/packages-dist/database-deprecated/index.js`, firestore: `${process.cwd()}/dist/packages-dist/firestore/index.js`, + functions: `${process.cwd()}/dist/packages-dist/functions/index.js`, storage: `${process.cwd()}/dist/packages-dist/storage/index.js` }; @@ -74,6 +79,7 @@ const SRC_PKG_PATHS = { "database-deprecated": `${process.cwd()}/src/database-deprecated/package.json`, firestore: `${process.cwd()}/src/firestore/package.json`, "firebase-node": `${process.cwd()}/src/firebase-node/package.json`, + functions: `${process.cwd()}/src/functions/package.json`, storage: `${process.cwd()}/src/storage/package.json` }; @@ -84,6 +90,7 @@ const DEST_PKG_PATHS = { "database-deprecated": `${process.cwd()}/dist/packages-dist/database-deprecated/package.json`, firestore: `${process.cwd()}/dist/packages-dist/firestore/package.json`, "firebase-node": `${process.cwd()}/dist/packages-dist/firebase-node/package.json`, + functions: `${process.cwd()}/dist/packages-dist/functions/package.json`, storage: `${process.cwd()}/dist/packages-dist/storage/package.json` }; @@ -92,6 +99,7 @@ const FIREBASE_FEATURE_MODULES = { auth: `${process.cwd()}/node_modules/@firebase/auth/dist/auth.js`, database: `${process.cwd()}/node_modules/@firebase/database/dist/esm/index.js`, firestore: `${process.cwd()}/node_modules/@firebase/firestore/dist/esm/index.js`, + functions: `${process.cwd()}/node_modules/@firebase/functions/dist/esm/index.js`, storage: `${process.cwd()}/node_modules/@firebase/storage/dist/esm/index.js`, util: `${process.cwd()}/node_modules/@firebase/util/dist/esm/index.js`, }; @@ -252,6 +260,7 @@ function getVersions() { getDestPackageFile('database'), getDestPackageFile('firestore'), getDestPackageFile('firebase-node'), + getDestPackageFile('functions'), getDestPackageFile('storage'), getDestPackageFile('database-deprecated') ]; @@ -290,12 +299,14 @@ function buildModules(globals) { const auth$ = buildModule('auth', globals); const db$ = buildModule('database', globals); const firestore$ = buildModule('firestore', globals); + const functions$ = buildModule('functions', globals); const storage$ = buildModule('storage', globals); const dbdep$ = buildModule('database-deprecated', globals); return forkJoin(core$, from(copyRootTest())).pipe( switchMapTo(auth$), switchMapTo(db$), switchMapTo(firestore$), + switchMapTo(functions$), switchMapTo(storage$), switchMapTo(dbdep$) ); @@ -315,6 +326,7 @@ function buildLibrary(globals) { const authStats = measure('auth'); const dbStats = measure('database'); const fsStats = measure('firestore'); + const functionsStats = measure('functions'); const storageStats = measure('storage'); const dbdepStats = measure('database-deprecated'); console.log(` @@ -322,6 +334,7 @@ function buildLibrary(globals) { auth.umd.js - ${authStats.size}, ${authStats.gzip} database.umd.js - ${dbStats.size}, ${dbStats.gzip} firestore.umd.js - ${fsStats.size}, ${fsStats.gzip} + functions.umd.js - ${functionsStats.size}, ${functionsStats.gzip} storage.umd.js - ${storageStats.size}, ${storageStats.gzip} database-deprecated.umd.js - ${dbdepStats.size}, ${dbdepStats.gzip} `); diff --git a/yarn.lock b/yarn.lock index 98792c2a2..3d8b4d445 100644 --- a/yarn.lock +++ b/yarn.lock @@ -101,11 +101,11 @@ grpc "1.10.1" tslib "1.9.0" -"@firebase/functions-types@0.1.2": +"@firebase/functions-types@0.1.2", "@firebase/functions-types@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.1.2.tgz#f291b2945218a97d9ab0e20f2fdec2cef6f90f94" -"@firebase/functions@0.2.2": +"@firebase/functions@0.2.2", "@firebase/functions@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.2.2.tgz#fd919deede5ec48870ecf751e4300066fc9424dc" dependencies: