From 28cb635f36430788cc502ca75d1c0e19bead0b38 Mon Sep 17 00:00:00 2001
From: Redian
Date: Sun, 8 May 2016 13:35:21 +0100
Subject: [PATCH 001/873] Update 1-install-and-setup.md (#157)
Updating the bootstrap instructions from `app.ts` -> `main.ts`
---
docs/1-install-and-setup.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/1-install-and-setup.md b/docs/1-install-and-setup.md
index af4e28df4..fb76cc566 100644
--- a/docs/1-install-and-setup.md
+++ b/docs/1-install-and-setup.md
@@ -92,7 +92,7 @@ AngularFire 2 and Firebase need to be mapped with System.js for module loading.
### 7. Bootstrap
-Open `/src/app.ts`, inject the Firebase providers, and specify your default Firebase:
+Open `/src/main.ts`, inject the Firebase providers, and specify your default Firebase:
```ts
import { bootstrap } from '@angular/platform-browser-dynamic';
@@ -177,4 +177,4 @@ Run the serve command and go to `localhost:4200` in your browser.
And that's it! If it totally borke, file an issue and let us know.
-###[Next Step: Retrieving data as objects](2-retrieving-data-as-objects.md)
\ No newline at end of file
+###[Next Step: Retrieving data as objects](2-retrieving-data-as-objects.md)
From 1086447b875dddcc9d28c6d14882429bd4f4f4ac Mon Sep 17 00:00:00 2001
From: Redian
Date: Sun, 8 May 2016 13:35:39 +0100
Subject: [PATCH 002/873] Update 1-install-and-setup.md (#158)
for...of loop update `let item of items`
---
docs/1-install-and-setup.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/1-install-and-setup.md b/docs/1-install-and-setup.md
index fb76cc566..0bcf5daf0 100644
--- a/docs/1-install-and-setup.md
+++ b/docs/1-install-and-setup.md
@@ -157,7 +157,7 @@ export class RcTestAppComponent {
Open `/src/app/.component.html`:
```html
-
+
{{item}}
From f5bab0157e4fe6dae442f308c3584a10f00e7c38 Mon Sep 17 00:00:00 2001
From: Redian
Date: Mon, 9 May 2016 12:36:37 +0100
Subject: [PATCH 003/873] 1-install-and-setup.md (#159)
Use `item.$value` otherwise it will print `[object Object]`
---
docs/1-install-and-setup.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/1-install-and-setup.md b/docs/1-install-and-setup.md
index 0bcf5daf0..40c8ee69d 100644
--- a/docs/1-install-and-setup.md
+++ b/docs/1-install-and-setup.md
@@ -159,7 +159,7 @@ Open `/src/app/.component.html`:
```html
- {{item}}
+ {{item.$value}}
```
From 0cd35fb8384ff6beb3614d8708ee0db0fe29e47d Mon Sep 17 00:00:00 2001
From: David East
Date: Mon, 9 May 2016 11:08:21 -0700
Subject: [PATCH 004/873] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 0640849fe..fc980dd28 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[](https://travis-ci.org/angular/angularfire2) [](https://gitter.im/angular/angularfire2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-Status: Alpha
+Status: Beta
## What is AngularFire2?
@@ -54,4 +54,4 @@ dangerous with AngularFire2.
4. [Querying lists](docs/4-querying-lists.md)
5. [User Authentication - FirebaseAuthentication](docs/5-user-authentication.md)
-## [API Reference](docs/api-reference.md)
\ No newline at end of file
+## [API Reference](docs/api-reference.md)
From 3edae9b064cb1b853666062c32eee4c8e7e79a3e Mon Sep 17 00:00:00 2001
From: David East
Date: Tue, 10 May 2016 10:08:22 -0700
Subject: [PATCH 005/873] fix(object): Unwrap snapshots with metadata fields.
(#164)
* fix(docs): system-config.ts setup
* fix(object) Unwrap objects and
* fix(query): Cast observable to list observable. Remove .list and .object from AngularFire.
* fix(object): Unwrap snapshots with metadata fields.
* fix(object): Unwrap object snapshots
* revert(lists): Remove Observable casting function.
* chore(changelog): Add Changelog
* chore(git): Poking git
* chore(git): Poking git
---
src/angularfire2.spec.ts | 88 +++-------------------
src/angularfire2.ts | 5 +-
src/utils/firebase_list_factory.spec.ts | 12 +--
src/utils/firebase_list_factory.ts | 19 +----
src/utils/firebase_list_observable.spec.ts | 6 +-
src/utils/firebase_object_factory.spec.ts | 18 ++++-
src/utils/firebase_object_factory.ts | 6 +-
src/utils/utils.ts | 13 ++++
8 files changed, 53 insertions(+), 114 deletions(-)
diff --git a/src/angularfire2.spec.ts b/src/angularfire2.spec.ts
index 938ac90e0..05f764dcc 100644
--- a/src/angularfire2.spec.ts
+++ b/src/angularfire2.spec.ts
@@ -18,7 +18,8 @@ import {
FirebaseAuth,
FirebaseUrl,
FirebaseRef,
- defaultFirebase
+ defaultFirebase,
+ FirebaseDatabase
} from './angularfire2';
import {Subscription} from 'rxjs';
import 'rxjs/add/operator/toPromise';
@@ -48,94 +49,23 @@ describe('angularfire', () => {
expect(injector.get(AngularFire)).toBeAnInstanceOf(AngularFire);
});
- describe('.list()', () => {
- var af:AngularFire;
+ describe('.auth', () => {
beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
- beforeEach(inject([AngularFire], (_af:AngularFire) => {
- af = _af;
- }));
-
-
- it('should accept an absolute url', () => {
- expect(af.list(localServerUrl)._ref.toString()).toEqual(localServerUrl);
- });
-
-
- it('should return an observable of the path', (done:any) => {
- var questions = af.list(`/questions`);
- questionsRef.push({pathObservable:true}, () => {
- subscription = questions
- .take(1)
- .do((data:any) => {
- expect(data.length).toBe(1);
- expect(data[0].pathObservable).toBe(true);
- })
- .subscribe(done, done.fail);
- });
- });
-
- it('should preserve snapshots in the list if preserveSnapshot option specified', (done:any) => {
- var questions = af.list(`list-of-questions`, {preserveSnapshot: true});
- listOfQuestionsRef.push('hello', () => {
- subscription = questions
- .take(1)
- .do((data:any) => {
- expect(data[0].val()).toEqual('hello');
- })
- .subscribe(done, done.fail);
- });
- });
- });
-
-
- describe('.object()', () => {
- var observable:FirebaseObjectObservable;
- var af:AngularFire;
-
- beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
- beforeEach(inject([AngularFire], (_af:AngularFire) => {
- observable = _af.object(`/list-of-questions/1`)
- af = _af;
+ it('should be an instance of AuthService', inject([AngularFire], (af:AngularFire) => {
+ expect(af.auth).toBeAnInstanceOf(FirebaseAuth);
}));
-
-
- it('should accept an absolute url', () => {
- expect((af.object(localServerUrl))._ref.toString()).toBe(localServerUrl);
- });
-
-
- it('should return an observable of the path', (done: any) => {
- return (observable)._ref.set({title: 'how to firebase?'})
- .then(() => observable.take(1).toPromise())
- .then((data:any) => {
- expect(data).toEqual({title: 'how to firebase?'});
- done();
- });
- });
-
-
- it('should preserve snapshot if preserveSnapshot option specified', (done: any) => {
- observable = af.object(`list-of-questions/`, {preserveSnapshot: true});
- return (observable)._ref.set({title: 'how to firebase?'})
- .then(() => observable.take(1).toPromise())
- .then((data:any) => {
- expect(data.val()).toEqual({title: 'how to firebase?'});
- done();
- });
- });
});
-
-
- describe('.auth', () => {
+
+
+ describe('.database', () => {
beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
it('should be an instance of AuthService', inject([AngularFire], (af:AngularFire) => {
- expect(af.auth).toBeAnInstanceOf(FirebaseAuth);
+ expect(af.database).toBeAnInstanceOf(FirebaseDatabase);
}));
});
-
describe('FIREBASE_REF', () => {
it('should provide a FirebaseRef for the FIREBASE_REF binding', () => {
var injector = ReflectiveInjector.resolveAndCreate([
diff --git a/src/angularfire2.ts b/src/angularfire2.ts
index d95b604d0..f02a6eca0 100644
--- a/src/angularfire2.ts
+++ b/src/angularfire2.ts
@@ -25,10 +25,7 @@ export class AngularFire {
constructor(
@Inject(FirebaseUrl) private fbUrl:string,
public auth:FirebaseAuth,
- public database: FirebaseDatabase) {
- this.list = database.list.bind(database);
- this.object = database.object.bind(database);
- }
+ public database: FirebaseDatabase) {}
}
diff --git a/src/utils/firebase_list_factory.spec.ts b/src/utils/firebase_list_factory.spec.ts
index a99814161..2c7153327 100644
--- a/src/utils/firebase_list_factory.spec.ts
+++ b/src/utils/firebase_list_factory.spec.ts
@@ -5,7 +5,6 @@ import {
onChildChanged,
onChildRemoved,
onChildUpdated,
- unwrapMapFn
} from './firebase_list_factory';
import {FirebaseListObservable} from './firebase_list_observable';
import {FirebaseObjectFactory} from './firebase_object_factory';
@@ -17,6 +16,7 @@ import {
describe,
expect
} from '@angular/core/testing';
+import * as utils from './utils';
import {Query} from './query_observable';
import {Subscription, Observable, Subject} from 'rxjs';
import 'rxjs/add/operator/do';
@@ -480,7 +480,7 @@ describe('FirebaseListFactory', () => {
});
- describe('unwrapMapFn', () => {
+ describe('utils.unwrapMapFn', () => {
var val = { unwrapped: true };
var snapshot = {
key: () => 'key',
@@ -488,7 +488,7 @@ describe('FirebaseListFactory', () => {
};
it('should return an object value with a $key property', () => {
- expect(unwrapMapFn(snapshot as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(snapshot as FirebaseDataSnapshot)).toEqual({
$key: 'key',
unwrapped: true
});
@@ -496,15 +496,15 @@ describe('FirebaseListFactory', () => {
it('should return an object value with a $value property if value is scalar', () => {
- expect(unwrapMapFn(Object.assign(snapshot, { val: () => 5 }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 5 }) as FirebaseDataSnapshot)).toEqual({
$key: 'key',
$value: 5
});
- expect(unwrapMapFn(Object.assign(snapshot, { val: () => false }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => false }) as FirebaseDataSnapshot)).toEqual({
$key: 'key',
$value: false
});
- expect(unwrapMapFn(Object.assign(snapshot, { val: () => 'lol' }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 'lol' }) as FirebaseDataSnapshot)).toEqual({
$key: 'key',
$value: 'lol'
});
diff --git a/src/utils/firebase_list_factory.ts b/src/utils/firebase_list_factory.ts
index 5ad4651f6..de9ff2b0d 100644
--- a/src/utils/firebase_list_factory.ts
+++ b/src/utils/firebase_list_factory.ts
@@ -102,7 +102,7 @@ function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot
// to better rendering performance
ref.once('value', (snap) => {
hasInitialLoad = true;
- obs.next(preserveSnapshot ? arr : arr.map(unwrapMapFn));
+ obs.next(preserveSnapshot ? arr : arr.map(utils.unwrapMapFn));
}, err => {
if (err) { obs.error(err); obs.complete(); }
});
@@ -111,7 +111,7 @@ function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot
arr = onChildAdded(arr, child, prevKey);
// only emit the array after the initial load
if (hasInitialLoad) {
- obs.next(preserveSnapshot ? arr : arr.map(unwrapMapFn));
+ obs.next(preserveSnapshot ? arr : arr.map(utils.unwrapMapFn));
}
}, err => {
if (err) { obs.error(err); obs.complete(); }
@@ -120,7 +120,7 @@ function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot
ref.on('child_removed', (child: any) => {
arr = onChildRemoved(arr, child)
if (hasInitialLoad) {
- obs.next(preserveSnapshot ? arr : arr.map(unwrapMapFn));
+ obs.next(preserveSnapshot ? arr : arr.map(utils.unwrapMapFn));
}
}, err => {
if (err) { obs.error(err); obs.complete(); }
@@ -130,7 +130,7 @@ function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot
arr = onChildChanged(arr, child, prevKey)
if (hasInitialLoad) {
// This also manages when the only change is prevKey change
- obs.next(preserveSnapshot ? arr : arr.map(unwrapMapFn));
+ obs.next(preserveSnapshot ? arr : arr.map(utils.unwrapMapFn));
}
}, err => {
if (err) { obs.error(err); obs.complete(); }
@@ -146,17 +146,6 @@ export interface FirebaseListFactoryOpts {
query?: Query;
}
-export function unwrapMapFn (snapshot:FirebaseDataSnapshot): AFUnwrappedDataSnapshot {
- var unwrapped = snapshot.val();
- if ((/string|number|boolean/).test(typeof unwrapped)) {
- unwrapped = {
- $value: unwrapped
- };
- }
- unwrapped.$key = snapshot.key();
- return unwrapped;
-}
-
export function onChildAdded(arr:any[], child:any, prevKey:string): any[] {
if (!arr.length) {
return [child];
diff --git a/src/utils/firebase_list_observable.spec.ts b/src/utils/firebase_list_observable.spec.ts
index a7bde5e3e..cab388ac3 100644
--- a/src/utils/firebase_list_observable.spec.ts
+++ b/src/utils/firebase_list_observable.spec.ts
@@ -1,9 +1,10 @@
import {describe,it,iit,beforeEach} from '@angular/core/testing';
import {FirebaseListObservable} from './firebase_list_observable';
import {Observer} from 'rxjs/Observer';
+import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import * as Firebase from 'firebase';
-import {unwrapMapFn} from './firebase_list_factory';
+import {unwrapMapFn} from './utils';
const rootUrl = 'https://angularfire2-list-obs.firebaseio-demo.com/';
@@ -28,8 +29,7 @@ describe('FirebaseObservable', () => {
});
expect(O.map(noop) instanceof FirebaseListObservable).toBe(true);
});
-
-
+
describe('push', () => {
it('should throw an exception if pushed when not subscribed', () => {
O = new FirebaseListObservable(null, (observer:Observer) => {});
diff --git a/src/utils/firebase_object_factory.spec.ts b/src/utils/firebase_object_factory.spec.ts
index 6d9e6d36c..acfb6a99c 100644
--- a/src/utils/firebase_object_factory.spec.ts
+++ b/src/utils/firebase_object_factory.spec.ts
@@ -40,12 +40,12 @@ describe('FirebaseObjectFactory', () => {
if (subscription && !subscription.isUnsubscribed) {
subscription.unsubscribe();
}
- })
+ });
it('should emit a null value if no value is present when subscribed', (done: any) => {
subscription = observable.subscribe(val => {
- expect(val).toBe(null);
+ expect(val).toEqual({ $key: (observable)._ref.key(), $value: null });
done();
});
});
@@ -54,12 +54,22 @@ describe('FirebaseObjectFactory', () => {
it('should emit unwrapped data by default', (done: any) => {
ref.set({ unwrapped: 'bar' }, () => {
subscription = observable.subscribe(val => {
- if (!val) return
- expect(val).toEqual({ unwrapped: 'bar' });
+ if (!val) return;
+ expect(val).toEqual({ $key: ref.key(), unwrapped: 'bar' });
done();
});
});
});
+
+ it('should emit unwrapped data with a $value property for primitive values', (done: any) => {
+ ref.set('fiiiireeee', () => {
+ subscription = observable.subscribe(val => {
+ if (!val) return;
+ expect(val).toEqual({ $key: ref.key(), $value: 'fiiiireeee' });
+ done();
+ });
+ });
+ });
it('should emit snapshots if preserveSnapshot option is true', (done: any) => {
diff --git a/src/utils/firebase_object_factory.ts b/src/utils/firebase_object_factory.ts
index 732ac9b83..68a7ed1e5 100644
--- a/src/utils/firebase_object_factory.ts
+++ b/src/utils/firebase_object_factory.ts
@@ -13,9 +13,9 @@ export function FirebaseObjectFactory(absoluteUrlOrDbRef: string | Firebase, {pr
isRef: () => ref = absoluteUrlOrDbRef
});
- return new FirebaseObjectObservable((obs: Observer) => {
- ref.on('value', (snapshot) => {
- obs.next(preserveSnapshot ? snapshot : snapshot.val())
+ return new FirebaseObjectObservable((obs: Observer) => {
+ ref.on('value', (snapshot: FirebaseDataSnapshot) => {
+ obs.next(preserveSnapshot ? snapshot : utils.unwrapMapFn(snapshot))
}, err => {
if (err) { obs.error(err); obs.complete(); }
});
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index eaf066c82..09218d773 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -1,3 +1,5 @@
+import { AFUnwrappedDataSnapshot} from './firebase_list_observable';
+
export function isPresent(obj: any): boolean {
return obj !== undefined && obj !== null;
}
@@ -33,6 +35,17 @@ export interface CheckUrlRef {
isQuery?: () => any;
}
+export function unwrapMapFn (snapshot:FirebaseDataSnapshot): AFUnwrappedDataSnapshot {
+ var unwrapped = isPresent(snapshot.val()) ? snapshot.val() : { $value: null };
+ if ((/string|number|boolean/).test(typeof unwrapped)) {
+ unwrapped = {
+ $value: unwrapped
+ };
+ }
+ unwrapped.$key = snapshot.key();
+ return unwrapped;
+}
+
export function checkForUrlOrFirebaseRef(urlOrRef: string | Firebase | FirebaseQuery, cases: CheckUrlRef): any {
if (isString(urlOrRef)) {
return cases.isUrl();
From 4b9caf521cda5f6200b6b19d36e41683a956b84d Mon Sep 17 00:00:00 2001
From: Redian
Date: Wed, 11 May 2016 16:16:15 +0100
Subject: [PATCH 006/873] Update 2-retrieving-data-as-objects.md (#166)
Remove an extra export statement.
---
docs/2-retrieving-data-as-objects.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/2-retrieving-data-as-objects.md b/docs/2-retrieving-data-as-objects.md
index 9fe87be7f..9b976e539 100644
--- a/docs/2-retrieving-data-as-objects.md
+++ b/docs/2-retrieving-data-as-objects.md
@@ -20,7 +20,7 @@ import { AngularFire } from 'angularfire2';
template: 'app.component.html',
styleUrls: ['app.component.css']
})
-export export class AppComponent {
+export class AppComponent {
constructor(af: AngularFire) {
}
From 7757e553caf202352f4d369a110455ec720b6301 Mon Sep 17 00:00:00 2001
From: Redian
Date: Thu, 12 May 2016 12:30:19 +0100
Subject: [PATCH 007/873] Update Retreive -> Retrieve (#169)
I always do this mistake, i am somehow happy i am not alone :)
---
docs/3-retrieving-data-as-lists.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/3-retrieving-data-as-lists.md b/docs/3-retrieving-data-as-lists.md
index e4f62d15e..f7bab15af 100644
--- a/docs/3-retrieving-data-as-lists.md
+++ b/docs/3-retrieving-data-as-lists.md
@@ -2,7 +2,7 @@
> AngularFire2 synchronizes data as lists using the `FirebaseListObservable`.
The `FirebaseListObservable` is not created by itself, but through the `AngularFire.database` service.
-The guide below demonstrates how to retreive, save, and remove data as lists.
+The guide below demonstrates how to retrieve, save, and remove data as lists.
## Injecting the AngularFire service
@@ -202,4 +202,4 @@ this.items
.subscribe(snapshots => console.log(snapshots.length));
```
-###[Next Step: Querying lists](4-querying-lists.md)
\ No newline at end of file
+###[Next Step: Querying lists](4-querying-lists.md)
From 99a580679c5d810e924ee3840f76def4f0eaf157 Mon Sep 17 00:00:00 2001
From: Redian
Date: Thu, 12 May 2016 12:30:37 +0100
Subject: [PATCH 008/873] Update 3-retrieving-data-as-lists.md (#170)
And in a few more places
---
docs/3-retrieving-data-as-lists.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/3-retrieving-data-as-lists.md b/docs/3-retrieving-data-as-lists.md
index f7bab15af..67e2b0214 100644
--- a/docs/3-retrieving-data-as-lists.md
+++ b/docs/3-retrieving-data-as-lists.md
@@ -27,7 +27,7 @@ class AppComponent {
## Create a list binding
-Data is retreived through the `af.database` service.
+Data is retrieved through the `af.database` service.
There are three ways to create an object binding:
@@ -49,7 +49,7 @@ const dbQuery = new Firebase('https://.firebaseio.com/item').limitToLa
const queryList = af.database.list(dbQuery);
```
-### Retreive data
+### Retrieve data
To get the list in realtime, create a list binding as a property of your component or service.
Then in your template, you can use the `async` pipe to unwrap the binding.
@@ -183,14 +183,14 @@ export class RcTestAppComponent {
```
## Meta-fields on the object
-Data retreived from the object binding contains special properties retreived from the unwrapped Firebase DataSnapshot.
+Data retrieved from the object binding contains special properties retrieved from the unwrapped Firebase DataSnapshot.
| property | |
| ---------|--------------------|
| $key | The key for each record. This is equivalent to each record's path in our database as it would be returned by `ref.key()`.|
| $value | If the data for this child node is a primitive (number, string, or boolean), then the record itself will still be an object. The primitive value will be stored under `$value` and can be changed and saved like any other field.|
-## Retreiving the snapshot
+## Retrieving the snapshot
AngularFire2 unwraps the Firebase DataSnapshot by default, but you can get the data as the original snapshot by specifying the `preserveSnapshot` option.
```ts
From 5040c403ca8258550a213b2b98265d63131fa69c Mon Sep 17 00:00:00 2001
From: Redian
Date: Thu, 12 May 2016 12:30:56 +0100
Subject: [PATCH 009/873] Updating 3-retrieving-data-as-lists.md (#171)
Pluralising the list names to make them a bit more intuitive.
---
docs/3-retrieving-data-as-lists.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/3-retrieving-data-as-lists.md b/docs/3-retrieving-data-as-lists.md
index 67e2b0214..ad494621d 100644
--- a/docs/3-retrieving-data-as-lists.md
+++ b/docs/3-retrieving-data-as-lists.md
@@ -38,14 +38,14 @@ There are three ways to create an object binding:
```ts
// relative URL, uses the database url provided in bootstrap
-const relative = af.database.list('/item');
+const relative = af.database.list('/items');
// absolute URL
-const absolute = af.database.list('https://.firebaseio.com/item');
+const absolute = af.database.list('https://.firebaseio.com/items');
// database reference
-const dbRef = new Firebase('https://.firebaseio.com/item');
+const dbRef = new Firebase('https://.firebaseio.com/items');
const relative = af.database.list(dbRef);
// query
-const dbQuery = new Firebase('https://.firebaseio.com/item').limitToLast(10);
+const dbQuery = new Firebase('https://.firebaseio.com/items').limitToLast(10);
const queryList = af.database.list(dbQuery);
```
From dc51962397c90ee14e6c8bd8b2e3d90d13194cc1 Mon Sep 17 00:00:00 2001
From: Redian
Date: Thu, 12 May 2016 12:31:55 +0100
Subject: [PATCH 010/873] Update 3-retrieving-data-as-lists.md (#172)
To be consistent with the Firebase api i suggest
to reorder the `update(newValue, key)` method parameters to `update(key, newValue)`
---
docs/3-retrieving-data-as-lists.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/3-retrieving-data-as-lists.md b/docs/3-retrieving-data-as-lists.md
index ad494621d..f41e63bfb 100644
--- a/docs/3-retrieving-data-as-lists.md
+++ b/docs/3-retrieving-data-as-lists.md
@@ -153,7 +153,7 @@ import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'a
@@ -170,7 +170,7 @@ export class RcTestAppComponent {
add(newName: string) {
this.items.push({ text: newName });
}
- update(newSize: string, key: string) {
+ update(key: string, newSize: string) {
this.items.update(key, { size: newSize });
}
deleteItem(key: string) {
From ba6f98d5ab211209604f7408646e2df7b6b64709 Mon Sep 17 00:00:00 2001
From: Chris Griffith
Date: Fri, 20 May 2016 14:27:03 -0700
Subject: [PATCH 011/873] Updating to RC1 modules (#183)
---
docs/3-retrieving-data-as-lists.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/3-retrieving-data-as-lists.md b/docs/3-retrieving-data-as-lists.md
index f41e63bfb..982dcbc98 100644
--- a/docs/3-retrieving-data-as-lists.md
+++ b/docs/3-retrieving-data-as-lists.md
@@ -11,7 +11,7 @@ The guide below demonstrates how to retrieve, save, and remove data as lists.
AngularFire is an injectable service, which is injected through the constructor of your Angular component or `@Injectable()` service.
```ts
-import {Component} from 'angular2/core';
+import {Component} from '@angular/core';
import {AngularFire} from 'angularfire2';
@Component({
@@ -55,14 +55,14 @@ To get the list in realtime, create a list binding as a property of your compone
Then in your template, you can use the `async` pipe to unwrap the binding.
```ts
-import {Component} from 'angular2/core';
+import {Component} from '@angular/core';
import {AngularFire, FirebaseListObservable} from 'angularfire2';
@Component({
selector: 'app',
templateUrl: `
@@ -100,7 +100,7 @@ from security rules denials, or for debugging.
const promise = af.database.list('/items').remove();
promise
.then(_ => console.log('success'))
- .catch(err => console.log(err, 'You dont have access!'));
+ .catch(err => console.log(err, 'You do not have access!'));
```
### Adding new items
From 5ccba51cbb55ea2e034d3ee838950a18d67d7b95 Mon Sep 17 00:00:00 2001
From: David East
Date: Fri, 20 May 2016 14:29:12 -0700
Subject: [PATCH 012/873] SDK version warning
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index fc980dd28..acfc4cd5a 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
The official library for Firebase and Angular 2
+### AngularFire2 is currently not on new Firebase 3.0 SDK, but there is an update in progress.
+
[](https://travis-ci.org/angular/angularfire2) [](https://gitter.im/angular/angularfire2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Status: Beta
From 6b6211bd1cc2f15a8cfe7a0964e193629ffce28d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tam=C3=A1s=20Gall=C3=B3?=
Date: Sun, 29 May 2016 16:49:37 +0200
Subject: [PATCH 013/873] Fix data binding expression in the example (#188)
- "item.name" is not an observable so we can't use the async pipe on it.
- use the safe navigation operator to access the name property of the retrieved object.
---
docs/2-retrieving-data-as-objects.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/2-retrieving-data-as-objects.md b/docs/2-retrieving-data-as-objects.md
index 9b976e539..0064b6a1e 100644
--- a/docs/2-retrieving-data-as-objects.md
+++ b/docs/2-retrieving-data-as-objects.md
@@ -59,7 +59,7 @@ import {AngularFire, FirebaseObjectObservable} from 'angularfire2';
@Component({
selector: 'app',
template: `
- {{ item.name | async }}
+ {{ (item | async)?.name }}
`,
})
export class AppComponent {
From d27bb17b09e5629c98b47595f5af9f9edd255b3c Mon Sep 17 00:00:00 2001
From: "Zhimin YE(Rex)"
Date: Sun, 29 May 2016 15:49:53 +0100
Subject: [PATCH 014/873] fix firebase version to 2.4.2 (#192)
Otherwise, firebase 3.x will be installed, which is not supported yet.
---
docs/1-install-and-setup.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/1-install-and-setup.md b/docs/1-install-and-setup.md
index 40c8ee69d..bcc9887d9 100644
--- a/docs/1-install-and-setup.md
+++ b/docs/1-install-and-setup.md
@@ -16,7 +16,7 @@ The Angular CLI's `new` command will set up the latest Angular build in a new pr
### 2. Install AngularFire 2 and Firebase
```bash
-npm install angularfire2 firebase --save
+npm install angularfire2 firebase@2.4.2 --save
```
Now that you have a new project setup, install AngularFire 2 and Firebase from npm.
From 6b3548f6aded2f7d51b6b1c0099a7ec005f23da3 Mon Sep 17 00:00:00 2001
From: Jek Bao CHOO
Date: Sun, 29 May 2016 22:50:56 +0800
Subject: [PATCH 015/873] docs: update Developer Guide to have consistency
(#194)
Minor inconsistency of naming. Moreover, including an explanation for item.$value and its relevant data structure would aid learners.
---
docs/1-install-and-setup.md | 20 ++++++++++++++++----
docs/2-retrieving-data-as-objects.md | 2 +-
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/docs/1-install-and-setup.md b/docs/1-install-and-setup.md
index bcc9887d9..815634003 100644
--- a/docs/1-install-and-setup.md
+++ b/docs/1-install-and-setup.md
@@ -112,7 +112,7 @@ bootstrap(, [
### 8. Inject AngularFire
-Open `/src/app/.component.ts`:
+Open `/src/app/.component.ts`:
```ts
import { Component } from '@angular/core';
@@ -134,7 +134,7 @@ export class Component {
### 9. Bind to a list
-In `/src/app/project-name.component.ts`:
+In `/src/app/.component.ts`:
```ts
import { Component } from '@angular/core';
@@ -146,7 +146,7 @@ import { AngularFire, FirebaseListObservable } from 'angularfire2';
templateUrl: '.component.html',
styleUrls: ['.component.css']
})
-export class RcTestAppComponent {
+export class Component {
items: FirebaseListObservable;
constructor(af: AngularFire) {
this.items = af.database.list('/items');
@@ -165,7 +165,19 @@ Open `/src/app/.component.html`:
```
The `async` pipe unwraps the each item in the people
-observable as they arrive.
+observable as they arrive. Also the array that is received through the `items` observable contains objects that have a `$value` property. A structure like this:
+```
+[
+ {
+ $value: 'something',
+ (...)
+ },
+ {
+ $value: 'something else',
+ (...)
+ },
+]
+```
### 10. Serve
diff --git a/docs/2-retrieving-data-as-objects.md b/docs/2-retrieving-data-as-objects.md
index 0064b6a1e..1edc224bd 100644
--- a/docs/2-retrieving-data-as-objects.md
+++ b/docs/2-retrieving-data-as-objects.md
@@ -63,7 +63,7 @@ import {AngularFire, FirebaseObjectObservable} from 'angularfire2';
`,
})
export class AppComponent {
- item: Observable;
+ item: FirebaseObjectObservable;
constructor(af: AngularFire) {
this.item = af.database.object('/item');
}
From b56982e1b5e3a447e5d07b5eb29cabea479b8fc6 Mon Sep 17 00:00:00 2001
From: Victor Berchet
Date: Sat, 4 Jun 2016 08:02:17 -0700
Subject: [PATCH 016/873] Misc minor changes (#205)
* refactor(providers): allow offline compilation
* chore(npm): automatically install typings on npm i
---
package.json | 3 ++-
src/angularfire2.ts | 23 +++++++++++++++++------
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/package.json b/package.json
index c6aa69bb6..2b5386328 100644
--- a/package.json
+++ b/package.json
@@ -8,8 +8,9 @@
"test": "npm run build; karma start",
"docs": "typedoc --out docs/api/ --module commonjs --mode modules --name AngularFire2 src",
"build": "rm -rf dist; tsc",
- "build_npm": "rm -rf dist && ./node_modules/.bin/tsc -p tsconfig.publish.es5.json && ./node_modules/.bin/tsc -p tsconfig.publish.es6.json",
+ "build_npm": "rm -rf dist && tsc -p tsconfig.publish.es5.json && tsc -p tsconfig.publish.es6.json",
"postbuild_npm": "cp package.json README.md .npmignore dist/ && npm run rewrite_npm_package",
+ "postinstall": "typings install",
"rewrite_npm_package": "node --harmony_destructuring tools/rewrite-published-package.js",
"build_bundle": "cp -r src angularfire2 && tsc typings/main.d.ts angularfire2.ts --rootDir . --module system -t es5 --outFile dist/bundles/angularfire2.js --moduleResolution node --emitDecoratorMetadata --experimentalDecorators"
},
diff --git a/src/angularfire2.ts b/src/angularfire2.ts
index f02a6eca0..cb1972272 100644
--- a/src/angularfire2.ts
+++ b/src/angularfire2.ts
@@ -38,22 +38,33 @@ function getAbsUrl (root:string, url:string) {
}
export const COMMON_PROVIDERS: any[] = [
- provide(FirebaseRef, {
- useFactory: (url:string) => new Firebase(url),
- deps: [FirebaseUrl]}),
+ {
+ provide: FirebaseRef,
+ useFactory: _getFirebase,
+ deps: [FirebaseUrl]
+ },
FirebaseAuth,
AngularFire,
FirebaseDatabase
];
+function _getFirebase(url:string): Firebase {
+ return new Firebase(url);
+}
+
export const FIREBASE_PROVIDERS:any[] = [
COMMON_PROVIDERS,
- provide(AuthBackend, {
- useFactory: (ref: Firebase) => new FirebaseSdkAuthBackend(ref, false),
+ {
+ provide: AuthBackend,
+ useFactory: _getAuthBackend,
deps: [FirebaseRef]
- })
+ }
];
+function _getAuthBackend(ref: Firebase): FirebaseSdkAuthBackend {
+ return new FirebaseSdkAuthBackend(ref, false);
+}
+
/**
* Used to define the default Firebase root location to be
* used throughout an application.
From 5fe4f545e83d87b8501005676c07281de0925111 Mon Sep 17 00:00:00 2001
From: David East
Date: Wed, 1 Jun 2016 13:34:16 -0700
Subject: [PATCH 017/873] chore(package): upgrade Firebase SDK and Typings to
3.x
---
karma-test-shim.js | 2 +-
karma.conf.js | 3 +-
manual_typings/firebase3/firebase3.d.ts | 415 ++++++++++++++++++++++++
manual_typings/manual_typings.d.ts | 1 +
package.json | 4 +-
tsconfig.json | 3 +-
tsconfig.publish.es5.json | 3 +-
tsconfig.publish.es6.json | 2 +-
typings.json | 5 +-
9 files changed, 428 insertions(+), 10 deletions(-)
create mode 100644 manual_typings/firebase3/firebase3.d.ts
diff --git a/karma-test-shim.js b/karma-test-shim.js
index 42f8e5866..184fb51d2 100644
--- a/karma-test-shim.js
+++ b/karma-test-shim.js
@@ -33,7 +33,7 @@ System.config(
map: {
'rxjs': 'node_modules/rxjs',
'@angular': 'node_modules/@angular',
- firebase: 'node_modules/firebase/lib/firebase-web.js',
+ firebase: 'node_modules/firebase/firebase.js',
'app': 'dist'
},
packages: {
diff --git a/karma.conf.js b/karma.conf.js
index 5009f68f6..33af0ec8a 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -34,8 +34,7 @@ module.exports = function(config) {
{pattern: 'node_modules/@angular/**/*.js', included: false, watched: true},
{pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: true},
- {pattern: 'node_modules/firebase/lib/firebase-web.js', included: false, watched: false},
- {pattern: 'node_modules/mock-promises/lib/mock-promises.js', included: false, watched: false},
+ {pattern: 'node_modules/firebase/firebase.js', included: false, watched: false},
{pattern: 'dist/**/*.js', included: false, watched: true},
{pattern: 'dist/**/*.js.map', included: false, watched: false}
],
diff --git a/manual_typings/firebase3/firebase3.d.ts b/manual_typings/firebase3/firebase3.d.ts
new file mode 100644
index 000000000..d4c9334ec
--- /dev/null
+++ b/manual_typings/firebase3/firebase3.d.ts
@@ -0,0 +1,415 @@
+/*
+
+1. Delete goog namespaces
+2. Delete look of disaproval
+3. typealias firebase.Promise to Promise
+4. Union type FirebaseOAuthProvider
+5. Remove _noStructuralTyping from Promise classes
+6. Remove catch() and then() declarations from firebase.Thenable, and extend Promise.
+
+*/
+declare interface FirebaseService {
+ INTERNAL: Object;
+ app: firebase.app.App;
+}
+
+declare interface FirebaseServiceNamespace {
+ app(app?: firebase.app.App): FirebaseService;
+}
+
+declare interface Observer {
+ complete(): any;
+ error(error: Object): any;
+ next(value: any): any;
+}
+
+
+declare type FirebaseOAuthProvider = firebase.auth.GithubAuthProvider |
+ firebase.auth.GoogleAuthProvider |
+ firebase.auth.FacebookAuthProvider;
+
+declare class Promise_Instance implements PromiseLike {
+ constructor(resolver: (a: (a?: TYPE | PromiseLike | { then: any }) => any, b: (a?: any) => any) => any);
+ catch(onRejected: (a: any) => RESULT): Promise;
+ then(opt_onFulfilled?: (a: TYPE) => VALUE, opt_onRejected?: (a: any) => any): RESULT;
+}
+
+declare namespace firebase {
+ type AuthTokenData = { accessToken: string, expirationTime: number, refreshToken: string };
+}
+declare namespace firebase {
+ type AuthTokenListener = (a: string) => void;
+}
+declare namespace firebase {
+ type CompleteFn = () => void;
+}
+declare namespace firebase {
+ type ErrorFn = (a: Object) => void;
+}
+declare namespace firebase {
+ interface FirebaseError {
+ code: string;
+ message: string;
+ name: string;
+ stack: string;
+ }
+}
+declare namespace firebase {
+ type NextFn = (a: any) => void;
+}
+declare namespace firebase {
+ class Promise extends Promise_Instance {
+ static all(values: firebase.Promise[]): firebase.Promise;
+ static reject(error: Object): firebase.Promise;
+ static resolve(value: T): firebase.Promise;
+ }
+ class Promise_Instance implements firebase.Thenable {
+ constructor(resolver: (a?: (a: T) => void, b?: (a: Object) => void) => any);
+ catch(onReject?: (a: Object) => any): firebase.Thenable;
+ then(onResolve?: (a: T) => any, onReject?: (a: Object) => any): firebase.Promise;
+ }
+}
+declare namespace firebase {
+ var SDK_VERSION: string;
+}
+declare namespace firebase {
+ type Subscribe = (a?: ((a: any) => void) | Observer, b?: (a: Object) => void, c?: () => void) => () => void;
+}
+declare namespace firebase {
+ interface Thenable extends Promise {
+ //Removed definitions of catch() and then(), and extended Promise.
+ }
+}
+declare namespace firebase {
+ type Unsubscribe = () => void;
+}
+declare namespace firebase {
+ interface User extends firebase.UserInfo {
+ delete(): firebase.Promise;
+ emailVerified: boolean;
+ getToken(opt_forceRefresh?: boolean): firebase.Promise;
+ isAnonymous: boolean;
+ link(credential: firebase.auth.AuthCredential): firebase.Promise;
+ linkWithPopup(provider: firebase.auth.AuthProvider): firebase.Promise<{ credential: firebase.auth.AuthCredential, user: firebase.User }>;
+ linkWithRedirect(provider: firebase.auth.AuthProvider): firebase.Promise;
+ providerData: (firebase.UserInfo)[];
+ reauthenticate(credential: firebase.auth.AuthCredential): firebase.Promise;
+ refreshToken: string;
+ reload(): firebase.Promise;
+ sendEmailVerification(): firebase.Promise;
+ unlink(providerId: string): firebase.Promise;
+ updateEmail(newEmail: string): firebase.Promise;
+ updatePassword(newPassword: string): firebase.Promise;
+ updateProfile(profile: { displayName: string, photoURL: string }): firebase.Promise;
+ }
+}
+declare namespace firebase {
+ interface UserInfo {
+ displayName: string;
+ email: string;
+ photoURL: string;
+ providerId: string;
+ uid: string;
+ }
+}
+declare namespace firebase {
+ function app(name: string): firebase.app.App;
+}
+declare namespace firebase.app {
+ interface App {
+ INTERNAL: Object;
+ auth(): firebase.auth.Auth;
+ database(): firebase.database.Database;
+ delete(): firebase.Promise;
+ name: string;
+ options: Object;
+ storage(): firebase.storage.Storage;
+ }
+}
+declare namespace firebase {
+ var apps: (firebase.app.App)[];
+}
+declare namespace firebase {
+ function auth(app?: firebase.app.App): firebase.auth.Auth;
+}
+declare namespace firebase.auth {
+ interface ActionCodeInfo {
+ }
+}
+declare namespace firebase.auth {
+ interface Auth {
+ app: firebase.app.App;
+ applyActionCode(code: string): firebase.Promise;
+ checkActionCode(code: string): firebase.Promise;
+ confirmPasswordReset(code: string, newPassword: string): firebase.Promise;
+ createUserWithEmailAndPassword(email: string, password: string): firebase.Promise;
+ currentUser: firebase.User;
+ fetchProvidersForEmail(email: string): firebase.Promise;
+ getRedirectResult(): firebase.Promise<{ credential: firebase.auth.AuthCredential, user: firebase.User }>;
+ onAuthStateChanged(nextOrObserver: Object, opt_error?: (a: firebase.auth.Error) => any, opt_completed?: () => any): () => any;
+ sendPasswordResetEmail(email: string): firebase.Promise;
+ signInAnonymously(): firebase.Promise;
+ signInWithCredential(credential: firebase.auth.AuthCredential): firebase.Promise;
+ signInWithCustomToken(token: string): firebase.Promise;
+ signInWithEmailAndPassword(email: string, password: string): firebase.Promise;
+ signInWithPopup(provider: firebase.auth.AuthProvider): firebase.Promise<{ credential: firebase.auth.AuthCredential, user: firebase.User }>;
+ signInWithRedirect(provider: firebase.auth.AuthProvider): firebase.Promise;
+ signOut(): firebase.Promise;
+ verifyPasswordResetCode(code: string): firebase.Promise;
+ }
+}
+declare namespace firebase.auth {
+ interface AuthCredential {
+ provider: string;
+ }
+}
+declare namespace firebase.auth {
+ interface AuthProvider {
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ class EmailAuthProvider extends EmailAuthProvider_Instance {
+ static PROVIDER_ID: string;
+ }
+ class EmailAuthProvider_Instance implements firebase.auth.AuthProvider {
+ private noStructuralTyping_: any;
+ credential(email: string, password: string): firebase.auth.AuthCredential;
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ interface Error {
+ code: string;
+ message: string;
+ }
+}
+declare namespace firebase.auth {
+ class FacebookAuthProvider extends FacebookAuthProvider_Instance {
+ static PROVIDER_ID: string;
+ }
+ class FacebookAuthProvider_Instance implements firebase.auth.AuthProvider {
+ private noStructuralTyping_: any;
+ addScope(scope: string): any;
+ credential(token: string): firebase.auth.AuthCredential;
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ class GithubAuthProvider extends GithubAuthProvider_Instance {
+ static PROVIDER_ID: string;
+ // TODO fix upstream
+ static credential(token: string): firebase.auth.AuthCredential;
+ }
+ class GithubAuthProvider_Instance implements firebase.auth.AuthProvider {
+ private noStructuralTyping_: any;
+ addScope(scope: string): any;
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ class GoogleAuthProvider extends GoogleAuthProvider_Instance {
+ static PROVIDER_ID: string;
+ }
+ class GoogleAuthProvider_Instance implements firebase.auth.AuthProvider {
+ private noStructuralTyping_: any;
+ addScope(scope: string): any;
+ credential(idToken?: string, accessToken?: string): firebase.auth.AuthCredential;
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ class TwitterAuthProvider extends TwitterAuthProvider_Instance {
+ static PROVIDER_ID: string;
+ // TODO fix this upstream
+ static credential(token: string, secret: string): firebase.auth.AuthCredential;
+ }
+ class TwitterAuthProvider_Instance implements firebase.auth.AuthProvider {
+ private noStructuralTyping_: any;
+ providerId: string;
+ }
+}
+declare namespace firebase.auth {
+ type UserCredential = { credential: firebase.auth.AuthCredential, user: firebase.User };
+}
+declare namespace firebase {
+ function database(app?: firebase.app.App): firebase.database.Database;
+}
+declare namespace firebase.database {
+ interface DataSnapshot {
+ child(path: string): firebase.database.DataSnapshot;
+ exists(): boolean;
+ exportVal(): any;
+ forEach(action: (a: firebase.database.DataSnapshot) => boolean): boolean;
+ getPriority(): string | number;
+ hasChild(path: string): boolean;
+ hasChildren(): boolean;
+ key: string;
+ numChildren(): number;
+ ref: firebase.database.Reference;
+ val(): any;
+ }
+}
+declare namespace firebase.database {
+ interface Database {
+ app: firebase.app.App;
+ goOffline(): any;
+ goOnline(): any;
+ ref(path?: string): firebase.database.Reference;
+ refFromURL(url: string): firebase.database.Reference;
+ }
+}
+declare namespace firebase.database {
+ interface OnDisconnect {
+ cancel(onComplete?: (a: Object) => any): firebase.Promise;
+ remove(onComplete?: (a: Object) => any): firebase.Promise;
+ set(value: any, onComplete?: (a: Object) => any): firebase.Promise;
+ setWithPriority(value: any, priority: number | string, onComplete?: (a: Object) => any): firebase.Promise;
+ update(values: Object, onComplete?: (a: Object) => any): firebase.Promise;
+ }
+}
+declare namespace firebase.database {
+ interface Query {
+ endAt(value: number | string | boolean, key?: string): firebase.database.Query;
+ equalTo(value: number | string | boolean, key?: string): firebase.database.Query;
+ limitToFirst(limit: number): firebase.database.Query;
+ limitToLast(limit: number): firebase.database.Query;
+ off(eventType?: string, callback?: (a: firebase.database.DataSnapshot, b?: string) => any, context?: Object): any;
+ on(eventType: string, callback: (a: firebase.database.DataSnapshot, b?: string) => any, cancelCallbackOrContext?: Object, context?: Object): (a: firebase.database.DataSnapshot, b?: string) => any;
+ once(eventType: string, callback?: (a: firebase.database.DataSnapshot, b?: string) => any): firebase.Promise;
+ orderByChild(path: string): firebase.database.Query;
+ orderByKey(): firebase.database.Query;
+ orderByPriority(): firebase.database.Query;
+ orderByValue(): firebase.database.Query;
+ ref: firebase.database.Reference;
+ startAt(value: number | string | boolean, key?: string): firebase.database.Query;
+ toString(): string;
+ }
+}
+declare namespace firebase.database {
+ interface Reference extends firebase.database.Query {
+ child(path: string): firebase.database.Reference;
+ key: string;
+ onDisconnect(): firebase.database.OnDisconnect;
+ parent: firebase.database.Reference;
+ push(value?: any, onComplete?: (a: Object) => any): firebase.database.ThenableReference;
+ remove(onComplete?: (a: Object) => any): firebase.Promise;
+ root: firebase.database.Reference;
+ set(value: any, onComplete?: (a: Object) => any): firebase.Promise;
+ setPriority(priority: string | number, onComplete: (a: Object) => any): firebase.Promise;
+ setWithPriority(newVal: any, newPriority: string | number, onComplete?: (a: Object) => any): firebase.Promise;
+ transaction(transactionUpdate: (a: any) => any, onComplete?: (a: Object, b: boolean, c: firebase.database.DataSnapshot) => any, applyLocally?: boolean): firebase.Promise<{ committed: boolean, snapshot: firebase.database.DataSnapshot }>;
+ update(values: Object, onComplete?: (a: Object) => any): firebase.Promise;
+ }
+}
+declare namespace firebase.database.ServerValue {
+}
+declare namespace firebase.database {
+ interface ThenableReference extends firebase.database.Reference, firebase.Thenable {
+ }
+}
+declare namespace firebase.database {
+ function enableLogging(logger?: any, persistent?: boolean): any;
+}
+declare namespace firebase {
+ function initializeApp(options: Object, name?: string): firebase.app.App;
+}
+declare namespace firebase {
+ function storage(app?: firebase.app.App): firebase.storage.Storage;
+}
+declare namespace firebase.storage {
+ interface FullMetadata extends firebase.storage.UploadMetadata {
+ bucket: string;
+ downloadURLs: string[];
+ fullPath: string;
+ generation: string;
+ metageneration: string;
+ name: string;
+ size: number;
+ timeCreated: string;
+ updated: string;
+ }
+}
+declare namespace firebase.storage {
+ interface Reference {
+ bucket: string;
+ child(path: string): firebase.storage.Reference;
+ delete(): Promise;
+ fullPath: string;
+ getDownloadURL(): Promise;
+ getMetadata(): Promise;
+ name: string;
+ parent: firebase.storage.Reference;
+ put(blob: Blob, metadata?: firebase.storage.UploadMetadata): firebase.storage.UploadTask;
+ root: firebase.storage.Reference;
+ storage: firebase.storage.Storage;
+ toString(): string;
+ updateMetadata(metadata: firebase.storage.SettableMetadata): Promise;
+ }
+}
+declare namespace firebase.storage {
+ interface SettableMetadata {
+ cacheControl: string;
+ contentDisposition: string;
+ contentEncoding: string;
+ contentLanguage: string;
+ contentType: string;
+ customMetadata: { [key: string]: string };
+ }
+}
+declare namespace firebase.storage {
+ interface Storage {
+ app: firebase.app.App;
+ maxOperationRetryTime: number;
+ maxUploadRetryTime: number;
+ ref(path?: string): firebase.storage.Reference;
+ refFromURL(url: string): firebase.storage.Reference;
+ setMaxOperationRetryTime(time: number): any;
+ setMaxUploadRetryTime(time: number): any;
+ }
+}
+declare namespace firebase.storage {
+ type TaskEvent = string;
+ var TaskEvent: {
+ STATE_CHANGED: TaskEvent,
+ };
+}
+declare namespace firebase.storage {
+ type TaskState = string;
+ var TaskState: {
+ CANCELED: TaskState,
+ ERROR: TaskState,
+ PAUSED: TaskState,
+ RUNNING: TaskState,
+ SUCCESS: TaskState,
+ };
+}
+declare namespace firebase.storage {
+ interface UploadMetadata extends firebase.storage.SettableMetadata {
+ md5Hash: string;
+ }
+}
+declare namespace firebase.storage {
+ interface UploadTask {
+ cancel(): boolean;
+ on(event: firebase.storage.TaskEvent, nextOrObserver?: Object, error?: (a: Object) => any, complete?: () => any): (...a: any[]) => any;
+ pause(): boolean;
+ resume(): boolean;
+ snapshot: firebase.storage.UploadTaskSnapshot;
+ }
+}
+declare namespace firebase.storage {
+ interface UploadTaskSnapshot {
+ bytesTransferred: number;
+ downloadURL: string;
+ metadata: firebase.storage.FullMetadata;
+ ref: firebase.storage.Reference;
+ state: firebase.storage.TaskState;
+ task: firebase.storage.UploadTask;
+ totalBytes: number;
+ }
+}
+
+declare module 'firebase' {
+ export = firebase;
+}
diff --git a/manual_typings/manual_typings.d.ts b/manual_typings/manual_typings.d.ts
index b5c0f58e1..11c113fd8 100644
--- a/manual_typings/manual_typings.d.ts
+++ b/manual_typings/manual_typings.d.ts
@@ -1 +1,2 @@
///
+///
diff --git a/package.json b/package.json
index 2b5386328..412122540 100644
--- a/package.json
+++ b/package.json
@@ -30,12 +30,12 @@
},
"homepage": "https://github.com/angular/indy#readme",
"dependencies": {
- "@angular/core": "2.0.0-rc.1",
+ "@angular/core": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1",
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
- "firebase": "2.4.2",
+ "firebase": "^3.0.3",
"rxjs": "5.0.0-beta.6"
},
"devDependencies": {
diff --git a/tsconfig.json b/tsconfig.json
index f77171092..9c810235e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,6 +22,7 @@
"src/utils/firebase_object_observable.spec.ts",
"src/utils/query_observable.spec.ts",
"src/providers/auth.spec.ts",
- "typings/main.d.ts"
+ "typings/main.d.ts",
+ "manual_typings/firebase3/firebase3.d.ts"
]
}
diff --git a/tsconfig.publish.es5.json b/tsconfig.publish.es5.json
index 1003c6c77..e40047f06 100644
--- a/tsconfig.publish.es5.json
+++ b/tsconfig.publish.es5.json
@@ -14,6 +14,7 @@
},
"files": [
"src/angularfire2.ts",
- "typings/main.d.ts"
+ "typings/main.d.ts",
+ "manual_typings/firebase3/firebase3.d.ts"
]
}
diff --git a/tsconfig.publish.es6.json b/tsconfig.publish.es6.json
index 7542dfcf1..6993912fb 100644
--- a/tsconfig.publish.es6.json
+++ b/tsconfig.publish.es6.json
@@ -14,6 +14,6 @@
},
"files": [
"src/angularfire2.ts",
- "typings/main/ambient/firebase/firebase.d.ts"
+ "manual_typings/firebase3/firebase3.d.ts"
]
}
diff --git a/typings.json b/typings.json
index 0d31312e6..0a9fcfdc0 100644
--- a/typings.json
+++ b/typings.json
@@ -1,9 +1,10 @@
{
- "dependencies": {},
+ "dependencies": {
+ "es6-promise": "github:typed-typings/npm-es6-promise#fb04188767acfec1defd054fc8024fafa5cd4de7"
+ },
"devDependencies": {},
"ambientDependencies": {
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2",
- "firebase": "github:DefinitelyTyped/DefinitelyTyped/firebase/firebase.d.ts#56295f5058cac7ae458540423c50ac2dcf9fc711",
"jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#64b25f63f0ec821040a5d3e049a976865062ed9d"
}
}
From d0e3fb9ad2a95e1f0e714a64ca05cec9f56ed484 Mon Sep 17 00:00:00 2001
From: Jeff Cross
Date: Mon, 13 Jun 2016 15:59:11 -0700
Subject: [PATCH 018/873] test(e2e): add more testing tools and auth e2e test
---
.gitignore | 8 +-
karma.conf.js | 6 +-
package.json | 11 +-
protractor.conf.js | 5 +-
src/test-config.ts | 6 +
test/e2e/auth/firebase_auth_example.spec.ts | 14 ++
test/e2e/auth/firebase_auth_example.ts | 140 ++++++++++++++++++
test/e2e/auth/index.html | 14 ++
.../firebase_list_example.spec.ts | 13 ++
.../firebase_list/firebase_list_example.ts | 25 ++--
test/e2e/firebase_list/index.html | 24 +--
.../firebase_object_example.spec.ts | 12 ++
.../firebase_object_example.ts | 24 +--
test/e2e/firebase_object/index.html | 24 +--
test/e2e/shared.ts | 5 +
test/e2e/system.config.ts | 45 ++++++
test/e2e/tsconfig.json | 16 --
test/tsconfig.json | 23 +++
test/typings.d.ts | 2 +
tools/test-server.js | 2 -
typings.json | 4 +-
21 files changed, 326 insertions(+), 97 deletions(-)
create mode 100644 src/test-config.ts
create mode 100644 test/e2e/auth/firebase_auth_example.spec.ts
create mode 100644 test/e2e/auth/firebase_auth_example.ts
create mode 100644 test/e2e/auth/index.html
create mode 100644 test/e2e/firebase_list/firebase_list_example.spec.ts
create mode 100644 test/e2e/firebase_object/firebase_object_example.spec.ts
create mode 100644 test/e2e/shared.ts
create mode 100644 test/e2e/system.config.ts
delete mode 100644 test/e2e/tsconfig.json
create mode 100644 test/tsconfig.json
create mode 100644 test/typings.d.ts
delete mode 100644 tools/test-server.js
diff --git a/.gitignore b/.gitignore
index db6aa54b0..1ec3efb7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,8 @@
node_modules/
dist/
+dist-test/
docs/api/
typings/
npm-debug.log
.idea/
-test/e2e/firebase_list/firebase_list_example.js
-test/e2e/firebase_list/firebase_list_example.js.map
-test/e2e/firebase_list/firebase_list_example.d.ts
-test/e2e/firebase_object/firebase_object_example.js
-test/e2e/firebase_object/firebase_object_example.js.map
-test/e2e/firebase_object/firebase_object_example.d.ts
+.vscode/settings.json
diff --git a/karma.conf.js b/karma.conf.js
index 33af0ec8a..8f6cf03b5 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -36,10 +36,10 @@ module.exports = function(config) {
{pattern: 'node_modules/firebase/firebase.js', included: false, watched: false},
{pattern: 'dist/**/*.js', included: false, watched: true},
- {pattern: 'dist/**/*.js.map', included: false, watched: false}
+ {pattern: 'dist/**/*.js.map', included: false, watched: false},
+ {pattern: 'src/**/*.ts', included: false, watched: false}
],
-
- reporters: ['mocha'],
+
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
diff --git a/package.json b/package.json
index 412122540..bcb5bda7a 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,12 @@
"test": "npm run build; karma start",
"docs": "typedoc --out docs/api/ --module commonjs --mode modules --name AngularFire2 src",
"build": "rm -rf dist; tsc",
- "build_npm": "rm -rf dist && tsc -p tsconfig.publish.es5.json && tsc -p tsconfig.publish.es6.json",
- "postbuild_npm": "cp package.json README.md .npmignore dist/ && npm run rewrite_npm_package",
- "postinstall": "typings install",
+ "build_npm": "rm -rf dist && ./node_modules/.bin/tsc -p tsconfig.publish.es5.json && ./node_modules/.bin/tsc -p tsconfig.publish.es6.json",
+ "postbuild_npm": "cp manual_typings/firebase3/firebase3.d.ts package.json README.md .npmignore dist/ && npm run rewrite_npm_package",
"rewrite_npm_package": "node --harmony_destructuring tools/rewrite-published-package.js",
- "build_bundle": "cp -r src angularfire2 && tsc typings/main.d.ts angularfire2.ts --rootDir . --module system -t es5 --outFile dist/bundles/angularfire2.js --moduleResolution node --emitDecoratorMetadata --experimentalDecorators"
+ "build_bundle": "cp -r src angularfire2 && tsc typings/main.d.ts angularfire2.ts --rootDir . --module system -t es5 --outFile dist/bundles/angularfire2.js --moduleResolution node --emitDecoratorMetadata --experimentalDecorators",
+ "e2e_test": "webdriver-manager update && npm run build_e2e && protractor",
+ "build_e2e": "npm run build && tsc -p test/ && cp test/e2e/firebase_object/index.html dist-test/e2e/firebase_object/ && cp test/e2e/firebase_list/index.html dist-test/e2e/firebase_list/ && cp test/e2e/auth/index.html dist-test/e2e/auth/"
},
"keywords": [
"angular2",
@@ -63,7 +64,7 @@
"traceur": "0.0.96",
"tsd": "^0.6.5",
"typedoc": "github:jeffbcross/typedoc",
- "typescript": "1.8.9",
+ "typescript": "^1.9.0-dev.20160608-1.0",
"typings": "^0.6.2",
"zone.js": "^0.6.6"
},
diff --git a/protractor.conf.js b/protractor.conf.js
index 123a6bc05..992fb8e75 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -1,7 +1,7 @@
var httpServer = require('http-server');
// An example configuration file.
exports.config = {
- baseUrl: 'http://localhost:8080/test/e2e/',
+ baseUrl: 'http://localhost:8080/dist-test/e2e/',
directConnect: true,
// seleniumAddress: 'http://localhost:4444/wd/hub',
@@ -16,7 +16,7 @@ exports.config = {
// Spec patterns are relative to the current working directly when
// protractor is called.
- specs: ['test/e2e/**/*.spec.js'],
+ specs: ['dist-test/**/*.spec.js'],
allScriptsTimeout: 110000,
onPrepare: function() {
@@ -26,7 +26,6 @@ exports.config = {
httpServer.createServer({
showDir: false
}).listen('8080', 'localhost');
- require('./tools/test-server');
},
// Options to be passed to Jasmine.
jasmineNodeOpts: {
diff --git a/src/test-config.ts b/src/test-config.ts
new file mode 100644
index 000000000..b2d9b8fb0
--- /dev/null
+++ b/src/test-config.ts
@@ -0,0 +1,6 @@
+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/test/e2e/auth/firebase_auth_example.spec.ts b/test/e2e/auth/firebase_auth_example.spec.ts
new file mode 100644
index 000000000..697515112
--- /dev/null
+++ b/test/e2e/auth/firebase_auth_example.spec.ts
@@ -0,0 +1,14 @@
+import { waitForElement } from '../shared';
+
+describe('FirebaseList', () => {
+ it('should login anonymously', () => {
+ browser.get('auth/index.html');
+ waitForElement('#is-anonymous');
+ var isAnonymous = $('#is-anonymous');
+ expect(isAnonymous.getText()).toBe('');
+ var login = $('#login-anonymous');
+ login.click();
+ browser.sleep(1000);
+ expect(isAnonymous.getText()).toBe('true');
+ });
+});
diff --git a/test/e2e/auth/firebase_auth_example.ts b/test/e2e/auth/firebase_auth_example.ts
new file mode 100644
index 000000000..02d1adc5a
--- /dev/null
+++ b/test/e2e/auth/firebase_auth_example.ts
@@ -0,0 +1,140 @@
+import {Component, enableProdMode, Inject, provide} from '@angular/core';
+import {bootstrap} from '@angular/platform-browser-dynamic';
+import {
+ AuthMethods,
+ AuthProviders,
+ defaultFirebase,
+ AngularFire,
+ FIREBASE_PROVIDERS,
+ FirebaseListObservable,
+ FirebaseApp
+} from '../../../dist/angularfire2';
+
+enableProdMode();
+
+import { COMMON_CONFIG } from '../../../src/test-config';
+
+@Component({
+ template: `
+ Logout
+
+
+ Anonymous Login
+ Login Anonymously
+
+
+ Is Anonymous?
+
+ {{ user?.isAnonymous }}
+
+
+
+
+ OAuth Login
+
+
+ OAuth Provider
+
+ Twitter
+ Facebook
+ Google
+ Github
+
+
+
+ Redirect?
+
+
+
+
+
+ Sign In
+
+
+
+
+
+ Create User
+
+
+ Create
+
+
+
+ Email and Password Login
+
+
+ Login
+
+
+
+ Custom Token Login
+
+ Login
+
+
+
+
+
+ Provider Data
+ {{ user?.providerData && user.providerData[0] | json }}
+ Not Logged In
+
+ `,
+ selector: 'app'
+})
+class App {
+ user: any;
+ questions: FirebaseListObservable;
+ constructor(public af: AngularFire) {
+ af.auth.onAuth()
+ .subscribe(user => {
+ console.log('onAuth', user);
+ this.user = user
+ });
+ }
+
+ signInAnonymously() {
+ this.af.auth.login({
+ method: AuthMethods.Anonymous
+ })
+ .then((user) => console.log(`Anonymous Login Success:`, user))
+ .catch(e => console.error(`Anonymous Login Failure:`, e));
+ }
+
+ signInWithOAuth(redirect: boolean, provider: string) {
+ this.af.auth.login({
+ method: redirect ? AuthMethods.Redirect : AuthMethods.Popup,
+ provider: (AuthProviders)[provider]
+ })
+ .then((user) => console.log(`${provider} Login Success:`, user))
+ .catch(e => console.error(`${provider} Login Failure:`, e));
+ }
+
+ createUser(email: string, password: string) {
+ this.af.auth.createUser({ email, password })
+ .then((user) => console.log(`Create User Success:`, user))
+ .catch(e => console.error(`Create User Failure:`, e));
+ }
+
+ loginUser(email: string, password: string) {
+ this.af.auth.login({ email, password }, {
+ method: AuthMethods.Password,
+ provider: AuthProviders.Password
+ })
+ .then((user) => console.log(`Password Login Success:`, user))
+ .catch(e => console.error(`Password Login Failure:`, e));
+ }
+
+ signInWithCustomToken(token: string) {
+ // TODO: this.af.auth.login({ token });
+ }
+}
+
+bootstrap(App, [
+ FIREBASE_PROVIDERS,
+ defaultFirebase(COMMON_CONFIG)]).then(() => {
+ console.log('bootstrap success');
+ }, (e:any) => {
+ console.error('bootstrap failed', e);
+ });
diff --git a/test/e2e/auth/index.html b/test/e2e/auth/index.html
new file mode 100644
index 000000000..01929560e
--- /dev/null
+++ b/test/e2e/auth/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/e2e/firebase_list/firebase_list_example.spec.ts b/test/e2e/firebase_list/firebase_list_example.spec.ts
new file mode 100644
index 000000000..82e348584
--- /dev/null
+++ b/test/e2e/firebase_list/firebase_list_example.spec.ts
@@ -0,0 +1,13 @@
+import { waitForElement } from '../shared';
+
+describe('FirebaseList', () => {
+ it('should render', () => {
+ browser.get('firebase_list/index.html');
+ waitForElement('app');
+ var app = $('app');
+ browser.sleep(500);
+ expect(app.getText()).toBe(`Hello
+why?
+how?`);
+ });
+});
\ No newline at end of file
diff --git a/test/e2e/firebase_list/firebase_list_example.ts b/test/e2e/firebase_list/firebase_list_example.ts
index 540f7b981..0c12e684c 100644
--- a/test/e2e/firebase_list/firebase_list_example.ts
+++ b/test/e2e/firebase_list/firebase_list_example.ts
@@ -1,16 +1,21 @@
import {Component, enableProdMode, Inject, provide} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
-import {defaultFirebase, AngularFire, FIREBASE_PROVIDERS, FirebaseListObservable} from 'angularfire2';
-import * as Firebase from 'firebase';
+import {
+ defaultFirebase,
+ AngularFire,
+ FIREBASE_PROVIDERS,
+ FirebaseListObservable,
+ FirebaseApp
+} from 'angularfire2';
enableProdMode();
-const rootFirebase = 'https://angularfire2.firebaseio-demo.com/';
+import { COMMON_CONFIG } from '../../../src/test-config';
@Component({
template: `
Hello
-
+
{{question.question}}
`,
@@ -18,18 +23,18 @@ const rootFirebase = 'https://angularfire2.firebaseio-demo.com/';
})
class App {
questions:FirebaseListObservable
;
- constructor(public af:AngularFire) {
- var ref = new Firebase(rootFirebase);
+ constructor(public af:AngularFire, @Inject(FirebaseApp) app: firebase.app.App) {
+ var ref = app.database().ref();
ref.remove();
- this.questions = af.list('/questions');
- this.questions.add({question: 'why?'});
- this.questions.add({question: 'how?'});
+ this.questions = af.database.list('/questions');
+ this.questions.push({question: 'why?'});
+ this.questions.push({question: 'how?'});
}
}
bootstrap(App, [
FIREBASE_PROVIDERS,
- defaultFirebase(rootFirebase)]).then(() => {
+ defaultFirebase(COMMON_CONFIG)]).then(() => {
console.log('bootstrap success');
}, (e:any) => {
console.error('bootstrap failed', e);
diff --git a/test/e2e/firebase_list/index.html b/test/e2e/firebase_list/index.html
index 5690b1660..d574734a4 100644
--- a/test/e2e/firebase_list/index.html
+++ b/test/e2e/firebase_list/index.html
@@ -1,31 +1,13 @@
-
-
-
+
+
+
diff --git a/test/e2e/firebase_object/firebase_object_example.spec.ts b/test/e2e/firebase_object/firebase_object_example.spec.ts
new file mode 100644
index 000000000..28a01f5e8
--- /dev/null
+++ b/test/e2e/firebase_object/firebase_object_example.spec.ts
@@ -0,0 +1,12 @@
+import { waitForElement } from '../shared';
+
+describe('FirebaseObject', () => {
+ it('should render', () => {
+ browser.get('firebase_object/index.html');
+ waitForElement('app');
+ var app = $('app');
+ browser.sleep(500);
+ expect(app.getText()).toBe(`Question
+how to firebase?`);
+ });
+});
\ No newline at end of file
diff --git a/test/e2e/firebase_object/firebase_object_example.ts b/test/e2e/firebase_object/firebase_object_example.ts
index 925f9c794..7c1dc9a42 100644
--- a/test/e2e/firebase_object/firebase_object_example.ts
+++ b/test/e2e/firebase_object/firebase_object_example.ts
@@ -1,13 +1,17 @@
import {Component, enableProdMode, Inject, provide} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
-import {defaultFirebase, AngularFire, FIREBASE_PROVIDERS, FirebaseObjectObservable} from 'angularfire2';
+import {
+ defaultFirebase,
+ AngularFire,
+ FIREBASE_PROVIDERS,
+ FirebaseObjectObservable,
+ FirebaseApp
+} from 'angularfire2';
-import * as Firebase from 'firebase';
+import { COMMON_CONFIG } from '../../../src/test-config';
enableProdMode();
-const rootFirebase = 'https://angularfire2.firebaseio-demo.com/';
-
@Component({
template: `
@@ -19,17 +23,19 @@ const rootFirebase = 'https://angularfire2.firebaseio-demo.com/';
})
class App {
question:FirebaseObjectObservable;
- constructor(public af:AngularFire) {
- var ref = new Firebase(rootFirebase);
+ constructor(public af:AngularFire, @Inject(FirebaseApp) app: firebase.app.App) {
+ var ref = app.database().ref();
ref.remove();
- this.question = af.object('/questions/1');
+ this.question = af.database.object('/questions/1');
ref.child('/questions/1').set({title: 'how to firebase?'});
}
}
bootstrap(App, [
- FIREBASE_PROVIDERS,
- defaultFirebase(rootFirebase)]).then(() => {
+ defaultFirebase(COMMON_CONFIG),
+ FIREBASE_PROVIDERS
+])
+ .then(() => {
console.log('bootstrap success');
}, (e:any) => {
console.error('bootstrap failed', e);
diff --git a/test/e2e/firebase_object/index.html b/test/e2e/firebase_object/index.html
index 0565b71e0..98542d1b0 100644
--- a/test/e2e/firebase_object/index.html
+++ b/test/e2e/firebase_object/index.html
@@ -1,31 +1,13 @@
-
-
-
+
+
+
diff --git a/test/e2e/shared.ts b/test/e2e/shared.ts
new file mode 100644
index 000000000..24e3bf06a
--- /dev/null
+++ b/test/e2e/shared.ts
@@ -0,0 +1,5 @@
+export function waitForElement(selector: string) {
+ var EC = (protractor).ExpectedConditions;
+ // Waits for the element with id 'abc' to be present on the dom.
+ browser.wait(EC.presenceOf($(selector)), 20000);
+}
\ No newline at end of file
diff --git a/test/e2e/system.config.ts b/test/e2e/system.config.ts
new file mode 100644
index 000000000..cb8ae128e
--- /dev/null
+++ b/test/e2e/system.config.ts
@@ -0,0 +1,45 @@
+declare var System: any;
+
+System.config({
+ defaultExtension: 'js',
+ map: {
+ 'firebase': '/node_modules/firebase/firebase.js',
+ 'angularfire2': '/dist',
+ 'typescript': '/node_modules/typescript/lib/typescript.js',
+ '@angular': '/node_modules/@angular',
+ 'rxjs': '/node_modules/rxjs',
+ 'test': '/dist-test/'
+ },
+ packages: {
+ 'test': {
+ defaultExtension: 'js'
+ },
+ 'rxjs': {
+ defaultExtension: 'js',
+ },
+ '@angular/core': {
+ main: 'index.js',
+ defaultExtension: 'js'
+ },
+ '@angular/common': {
+ main: 'index.js',
+ defaultExtension: 'js'
+ },
+ '@angular/compiler': {
+ main: 'index.js',
+ defaultExtension: 'js'
+ },
+ '@angular/platform-browser': {
+ main: 'index.js',
+ defaultExtension: 'js'
+ },
+ '@angular/platform-browser-dynamic': {
+ main: 'index.js',
+ defaultExtension: 'js'
+ },
+ 'angularfire2': {
+ main: 'angularfire2.js',
+ defaultExtension: 'js'
+ }
+ }
+});
diff --git a/test/e2e/tsconfig.json b/test/e2e/tsconfig.json
deleted file mode 100644
index 79e4a8347..000000000
--- a/test/e2e/tsconfig.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "compilerOptions": {
- "experimentalDecorators": true,
- "emitDecoratorMetadata": true,
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": true,
- "sourceMap": true,
- "declaration": true,
- "removeComments": true
- },
- "files": [
- "firebase_list/firebase_list_example.ts",
- "firebase_object/firebase_object_example.ts"
- ]
-}
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 000000000..902041ae1
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "module": "commonjs",
+ "target": "es5",
+ "noImplicitAny": true,
+ "sourceMap": true,
+ "declaration": true,
+ "removeComments": true,
+ "baseUrl": ".",
+ "outDir": "../dist-test",
+ "paths": {
+ "angularfire2": [
+ "../dist/*",
+ "../dist/angularfire2.d.ts"
+ ],
+ "test/config": [
+ "./config.ts"
+ ]
+ }
+ }
+}
diff --git a/test/typings.d.ts b/test/typings.d.ts
new file mode 100644
index 000000000..b1db1096c
--- /dev/null
+++ b/test/typings.d.ts
@@ -0,0 +1,2 @@
+///
+///
diff --git a/tools/test-server.js b/tools/test-server.js
deleted file mode 100644
index e9b9facf9..000000000
--- a/tools/test-server.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var FirebaseServer = require('firebase-server');
-new FirebaseServer(5000, 'localhost.firebaseio.test:5000');
diff --git a/typings.json b/typings.json
index 0a9fcfdc0..b843a5753 100644
--- a/typings.json
+++ b/typings.json
@@ -4,7 +4,9 @@
},
"devDependencies": {},
"ambientDependencies": {
+ "angular-protractor": "github:DefinitelyTyped/DefinitelyTyped/angular-protractor/angular-protractor.d.ts#f9c44651705f574f6d4258fe5e1c335462bdcc19",
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2",
- "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#64b25f63f0ec821040a5d3e049a976865062ed9d"
+ "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#64b25f63f0ec821040a5d3e049a976865062ed9d",
+ "selenium-webdriver": "github:DefinitelyTyped/DefinitelyTyped/selenium-webdriver/selenium-webdriver.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd"
}
}
From 49d015df3d958293b1b219e89ddd36c5dcb36797 Mon Sep 17 00:00:00 2001
From: Jeff Cross
Date: Mon, 13 Jun 2016 16:01:47 -0700
Subject: [PATCH 019/873] fix(AngularFire): update main entry point to use new
Firebase API
---
src/angularfire2.spec.ts | 79 ++++++++++++++++---------------
src/angularfire2.ts | 60 ++++++++++-------------
src/angularfire2_worker_render.ts | 6 +--
src/interfaces.ts | 6 +++
src/tokens.ts | 4 +-
src/utils/utils.ts | 30 +++++++++---
6 files changed, 101 insertions(+), 84 deletions(-)
create mode 100644 src/interfaces.ts
diff --git a/src/angularfire2.spec.ts b/src/angularfire2.spec.ts
index 05f764dcc..3bfd858d3 100644
--- a/src/angularfire2.spec.ts
+++ b/src/angularfire2.spec.ts
@@ -15,11 +15,12 @@ import {
AngularFire,
FirebaseObjectObservable,
FIREBASE_PROVIDERS,
- FirebaseAuth,
- FirebaseUrl,
- FirebaseRef,
+ AngularFireAuth,
+ FirebaseConfig,
+ FirebaseApp,
defaultFirebase,
- FirebaseDatabase
+ FirebaseDatabase,
+ FirebaseAppConfig
} from './angularfire2';
import {Subscription} from 'rxjs';
import 'rxjs/add/operator/toPromise';
@@ -27,68 +28,70 @@ import 'rxjs/add/operator/take';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/delay';
-// Only use this URL for angularfire2.spec.ts
-const localServerUrl = 'https://angularfire2.firebaseio-demo.com/';
+export const firebaseConfig: FirebaseAppConfig = {
+ apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA",
+ authDomain: "angularfire2-test.firebaseapp.com",
+ databaseURL: "https://angularfire2-test.firebaseio.com/",
+ storageBucket: "angularfire2-test.appspot.com",
+};
describe('angularfire', () => {
var subscription:Subscription;
- const questionsRef = new Firebase(localServerUrl).child('questions');
- const listOfQuestionsRef = new Firebase(localServerUrl).child('list-of-questions');
+ var app: firebase.app.App;
+ var rootRef: firebase.database.Reference;
+ var questionsRef: firebase.database.Reference;
+ var listOfQuestionsRef: firebase.database.Reference;
+ var angularFire2: AngularFire;
- afterEach((done:any) => {
- // Clear out the Firebase to prevent leaks between tests
- (new Firebase(localServerUrl)).remove(done);
+ beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(firebaseConfig)]);
+
+ describe('things', () => {
+
+ })
+ beforeEach(inject([FirebaseApp, AngularFire], (firebaseApp: firebase.app.App, _af: AngularFire) => {
+ angularFire2 = _af;
+ app = firebaseApp;
+ rootRef = app.database().ref();
+ questionsRef = rootRef.child('questions');
+ listOfQuestionsRef = rootRef.child('list-of-questions');
+ }));
+
+ afterEach((done) => {
+ rootRef.remove()
if(subscription && !subscription.isUnsubscribed) {
subscription.unsubscribe();
}
+ app.delete().then(done, done.fail);
});
it('should be injectable via FIREBASE_PROVIDERS', () => {
- var injector = ReflectiveInjector.resolveAndCreate([FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
- expect(injector.get(AngularFire)).toBeAnInstanceOf(AngularFire);
+ expect(angularFire2).toBeAnInstanceOf(AngularFire);
});
describe('.auth', () => {
- beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
-
it('should be an instance of AuthService', inject([AngularFire], (af:AngularFire) => {
- expect(af.auth).toBeAnInstanceOf(FirebaseAuth);
+ expect(af.auth).toBeAnInstanceOf(AngularFireAuth);
}));
});
-
-
- describe('.database', () => {
- beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(localServerUrl)]);
- it('should be an instance of AuthService', inject([AngularFire], (af:AngularFire) => {
+
+ describe('.database', () => {
+ it('should be an instance of Database', inject([AngularFire], (af:AngularFire) => {
expect(af.database).toBeAnInstanceOf(FirebaseDatabase);
}));
});
- describe('FIREBASE_REF', () => {
- it('should provide a FirebaseRef for the FIREBASE_REF binding', () => {
- var injector = ReflectiveInjector.resolveAndCreate([
- provide(FirebaseUrl, {
- useValue: localServerUrl
- }),
- FIREBASE_PROVIDERS
- ]);
- expect(typeof injector.get(FirebaseRef).on).toBe('function');
+ describe('FirebaseApp', () => {
+ it('should provide a FirebaseApp for the FirebaseApp binding', () => {
+ expect(typeof app.delete).toBe('function');
})
});
describe('defaultFirebase', () => {
it('should create a provider', () => {
- const provider = defaultFirebase(localServerUrl);
+ const provider = defaultFirebase(firebaseConfig);
expect(provider).toBeAnInstanceOf(Provider);
});
-
-
- it('should inject a FIR reference', () => {
- const injector = ReflectiveInjector.resolveAndCreate([defaultFirebase(localServerUrl), FIREBASE_PROVIDERS]);
- expect(injector.get(FirebaseRef).toString()).toBe(localServerUrl);
- });
});
-
});
diff --git a/src/angularfire2.ts b/src/angularfire2.ts
index cb1972272..4d5cad7a1 100644
--- a/src/angularfire2.ts
+++ b/src/angularfire2.ts
@@ -1,6 +1,6 @@
import {APP_INITIALIZER, Inject, Injectable, OpaqueToken, provide, Provider} from '@angular/core';
-import {FirebaseAuth, firebaseAuthConfig} from './providers/auth';
-import * as Firebase from 'firebase';
+import {AngularFireAuth, firebaseAuthConfig} from './providers/auth';
+import { initializeApp } from 'firebase';
import {FirebaseListObservable} from './utils/firebase_list_observable';
import {FirebaseObjectObservable} from './utils/firebase_object_observable';
import {FirebaseListFactory, FirebaseListFactoryOpts} from './utils/firebase_list_factory';
@@ -8,7 +8,9 @@ import {
FirebaseObjectFactoryOpts,
FirebaseObjectFactory
} from './utils/firebase_object_factory';
-import {FirebaseUrl, FirebaseRef} from './tokens';
+import * as utils from './utils/utils';
+import {FirebaseConfig, FirebaseApp} from './tokens';
+import { FirebaseAppConfig } from './interfaces';
import {
AuthBackend,
AuthMethods,
@@ -23,27 +25,18 @@ export class AngularFire {
list: (url:string, opts?:FirebaseListFactoryOpts) => FirebaseListObservable;
object: (url: string, opts?:FirebaseObjectFactoryOpts) => FirebaseObjectObservable;
constructor(
- @Inject(FirebaseUrl) private fbUrl:string,
- public auth:FirebaseAuth,
+ @Inject(FirebaseConfig) private fbUrl:string,
+ public auth: AngularFireAuth,
public database: FirebaseDatabase) {}
-
-}
-
-function getAbsUrl (root:string, url:string) {
- if (!(/^[a-z]+:\/\/.*/.test(url))) {
- // Provided url is relative.
- url = root + url;
- }
- return url;
}
export const COMMON_PROVIDERS: any[] = [
- {
- provide: FirebaseRef,
- useFactory: _getFirebase,
- deps: [FirebaseUrl]
- },
- FirebaseAuth,
+ provide(FirebaseApp, {
+ useFactory: (config: FirebaseAppConfig) => {
+ return initializeApp(config);
+ },
+ deps: [FirebaseConfig]}),
+ AngularFireAuth,
AngularFire,
FirebaseDatabase
];
@@ -54,29 +47,26 @@ function _getFirebase(url:string): Firebase {
export const FIREBASE_PROVIDERS:any[] = [
COMMON_PROVIDERS,
- {
- provide: AuthBackend,
- useFactory: _getAuthBackend,
- deps: [FirebaseRef]
- }
+ provide(AuthBackend, {
+ useFactory: (app: firebase.app.App) => new FirebaseSdkAuthBackend(app, false),
+ deps: [FirebaseApp]
+ })
];
-function _getAuthBackend(ref: Firebase): FirebaseSdkAuthBackend {
- return new FirebaseSdkAuthBackend(ref, false);
-}
-
/**
* Used to define the default Firebase root location to be
* used throughout an application.
*/
-export const defaultFirebase = (url: string): Provider => {
- return provide(FirebaseUrl, {
- useValue: url
+export const defaultFirebase = (config: FirebaseAppConfig): Provider => {
+ // remove a trailing slash from the Database URL if it exists
+ config.databaseURL = utils.stripTrailingSlash(config.databaseURL);
+ return provide(FirebaseConfig, {
+ useValue: config
});
};
export {
- FirebaseAuth,
+ AngularFireAuth,
FirebaseDatabase,
FirebaseListObservable,
FirebaseObjectObservable,
@@ -88,10 +78,10 @@ export {
AuthProviders
}
-export {FirebaseUrl, FirebaseRef, FirebaseAuthConfig} from './tokens';
+export { FirebaseConfig, FirebaseApp, FirebaseAuthConfig } from './tokens';
+export { FirebaseAppConfig } from './interfaces';
// Helps Angular-CLI automatically add providers
export default {
providers: FIREBASE_PROVIDERS
}
-
diff --git a/src/angularfire2_worker_render.ts b/src/angularfire2_worker_render.ts
index 32a8d37fd..6114aae83 100644
--- a/src/angularfire2_worker_render.ts
+++ b/src/angularfire2_worker_render.ts
@@ -2,14 +2,14 @@ import {provide} from '@angular/core';
import {COMMON_PROVIDERS} from './angularfire2';
import {FirebaseSdkAuthBackend} from './providers/firebase_sdk_auth_backend';
import {WebWorkerFirebaseAuth} from './providers/web_workers/worker/auth';
-import {FirebaseRef} from './tokens';
+import {FirebaseApp} from './tokens';
import {MessageBasedFirebaseAuth} from './providers/web_workers/ui/auth';
export const WORKER_RENDER_FIREBASE_PROVIDERS: any[] = [
COMMON_PROVIDERS,
provide(FirebaseSdkAuthBackend, {
- useFactory: (ref: Firebase) => new FirebaseSdkAuthBackend(ref, true),
- deps: [FirebaseRef]
+ useFactory: (app) => new FirebaseSdkAuthBackend(app, true),
+ deps: [FirebaseApp]
}),
MessageBasedFirebaseAuth
];
diff --git a/src/interfaces.ts b/src/interfaces.ts
new file mode 100644
index 000000000..12ef624f2
--- /dev/null
+++ b/src/interfaces.ts
@@ -0,0 +1,6 @@
+export interface FirebaseAppConfig {
+ apiKey: string;
+ authDomain: string;
+ databaseURL: string;
+ storageBucket: string;
+}
diff --git a/src/tokens.ts b/src/tokens.ts
index 46e2efed2..608b7fd8c 100644
--- a/src/tokens.ts
+++ b/src/tokens.ts
@@ -1,5 +1,5 @@
import {OpaqueToken} from '@angular/core';
-export const FirebaseUrl = new OpaqueToken('FirebaseUrl');
-export const FirebaseRef = new OpaqueToken('FirebaseRef')
+export const FirebaseConfig = new OpaqueToken('FirebaseUrl');
+export const FirebaseApp = new OpaqueToken('FirebaseApp')
export const FirebaseAuthConfig = new OpaqueToken('FirebaseAuthConfig');
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 09218d773..53c3f798c 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -9,11 +9,11 @@ export function isString(value: any): boolean {
}
export function isFirebaseRef(value: any): boolean {
- return value instanceof Firebase;
+ return typeof value.set === 'function';
}
export function isFirebaseDataSnapshot(value: any): boolean {
- return typeof value.key === 'function';
+ return typeof value.exportVal === 'function';
}
export function isAFUnwrappedSnapshot(value: any): boolean {
@@ -35,18 +35,18 @@ export interface CheckUrlRef {
isQuery?: () => any;
}
-export function unwrapMapFn (snapshot:FirebaseDataSnapshot): AFUnwrappedDataSnapshot {
+export function unwrapMapFn (snapshot:firebase.database.DataSnapshot): AFUnwrappedDataSnapshot {
var unwrapped = isPresent(snapshot.val()) ? snapshot.val() : { $value: null };
if ((/string|number|boolean/).test(typeof unwrapped)) {
unwrapped = {
$value: unwrapped
};
}
- unwrapped.$key = snapshot.key();
+ unwrapped.$key = snapshot.ref.key;
return unwrapped;
}
-export function checkForUrlOrFirebaseRef(urlOrRef: string | Firebase | FirebaseQuery, cases: CheckUrlRef): any {
+export function checkForUrlOrFirebaseRef(urlOrRef: string | firebase.database.Reference | firebase.database.Query, cases: CheckUrlRef): any {
if (isString(urlOrRef)) {
return cases.isUrl();
}
@@ -56,5 +56,23 @@ export function checkForUrlOrFirebaseRef(urlOrRef: string | Firebase | FirebaseQ
if (isFirebaseQuery(urlOrRef)) {
return cases.isQuery();
}
- throw new Error('Provide a url or a Firebase database reference');
+ throw new Error('Provide a url or a Firebase database reference');
+}
+
+export function stripTrailingSlash(value: string): string {
+ // Is the last char a /
+ if (value.substring(value.length - 1, value.length) === '/') {
+ return value.substring(0, value.length - 1);
+ } else {
+ return value;
+ }
+}
+
+export function stripLeadingSlash(value: string): string {
+ // Is the last char a /
+ if (value.substring(0, 1) === '/') {
+ return value.substring(1, value.length);
+ } else {
+ return value;
+ }
}
\ No newline at end of file
From 5bde0578545ecb8434163900fa75f7a522f9cc3b Mon Sep 17 00:00:00 2001
From: David East
Date: Mon, 13 Jun 2016 16:03:15 -0700
Subject: [PATCH 020/873] fix(database): make database work with new Firebase
SDK
---
src/database/database.ts | 22 +--
src/utils/firebase_list_factory.spec.ts | 167 +++++++++++--------
src/utils/firebase_list_factory.ts | 73 ++++----
src/utils/firebase_list_observable.spec.ts | 105 +++++++-----
src/utils/firebase_list_observable.ts | 61 +++----
src/utils/firebase_object_factory.spec.ts | 58 +++++--
src/utils/firebase_object_factory.ts | 14 +-
src/utils/firebase_object_observable.spec.ts | 90 ++++++----
src/utils/firebase_object_observable.ts | 8 +-
9 files changed, 352 insertions(+), 246 deletions(-)
diff --git a/src/database/database.ts b/src/database/database.ts
index 4a3d04ef3..4cb983622 100644
--- a/src/database/database.ts
+++ b/src/database/database.ts
@@ -1,5 +1,6 @@
import {Inject, Injectable} from '@angular/core';
-import {FirebaseUrl} from '../tokens';
+import {FirebaseConfig} from '../tokens';
+import {FirebaseAppConfig} from '../angularfire2';
import {FirebaseListObservable} from '../utils/firebase_list_observable';
import {FirebaseObjectObservable} from '../utils/firebase_object_observable';
import {FirebaseListFactory, FirebaseListFactoryOpts} from '../utils/firebase_list_factory';
@@ -8,25 +9,26 @@ import * as utils from '../utils/utils'
@Injectable()
export class FirebaseDatabase {
- constructor(@Inject(FirebaseUrl) private fbUrl:string) {}
- list (urlOrRef:string | Firebase, opts?:FirebaseListFactoryOpts):FirebaseListObservable {
+ constructor(@Inject(FirebaseConfig) private fbConfig:FirebaseAppConfig) {}
+ list (urlOrRef:string | firebase.database.Reference, opts?:FirebaseListFactoryOpts):FirebaseListObservable {
return utils.checkForUrlOrFirebaseRef(urlOrRef, {
- isUrl: () => FirebaseListFactory(getAbsUrl(this.fbUrl, urlOrRef), opts),
- isRef: () => FirebaseListFactory(urlOrRef)
+ isUrl: () => FirebaseListFactory(getAbsUrl(this.fbConfig, urlOrRef), opts),
+ isRef: () => FirebaseListFactory(urlOrRef)
});
}
- object(urlOrRef: string | Firebase, opts?:FirebaseObjectFactoryOpts):FirebaseObjectObservable {
+ object(urlOrRef: string | firebase.database.Reference, opts?:FirebaseObjectFactoryOpts):FirebaseObjectObservable {
return utils.checkForUrlOrFirebaseRef(urlOrRef, {
- isUrl: () => FirebaseObjectFactory(getAbsUrl(this.fbUrl, urlOrRef), opts),
+ isUrl: () => FirebaseObjectFactory(getAbsUrl(this.fbConfig, urlOrRef), opts),
isRef: () => FirebaseObjectFactory(urlOrRef)
});
}
}
-function getAbsUrl (root:string, url:string) {
+function getAbsUrl (root:FirebaseAppConfig, url:string) {
if (!(/^[a-z]+:\/\/.*/.test(url))) {
// Provided url is relative.
- url = root + url;
+ // Strip any leading slash
+ url = root.databaseURL + '/' + utils.stripLeadingSlash(url);
}
return url;
-}
\ No newline at end of file
+}
diff --git a/src/utils/firebase_list_factory.spec.ts b/src/utils/firebase_list_factory.spec.ts
index 2c7153327..04bb4d64b 100644
--- a/src/utils/firebase_list_factory.spec.ts
+++ b/src/utils/firebase_list_factory.spec.ts
@@ -8,13 +8,22 @@ import {
} from './firebase_list_factory';
import {FirebaseListObservable} from './firebase_list_observable';
import {FirebaseObjectFactory} from './firebase_object_factory';
+import {
+ FIREBASE_PROVIDERS,
+ defaultFirebase,
+ FirebaseApp,
+ FirebaseAppConfig,
+ AngularFire
+} from '../angularfire2';
import {
beforeEach,
it,
iit,
ddescribe,
describe,
- expect
+ expect,
+ beforeEachProviders,
+ inject
} from '@angular/core/testing';
import * as utils from './utils';
import {Query} from './query_observable';
@@ -23,7 +32,13 @@ import 'rxjs/add/operator/do';
import 'rxjs/add/operator/skip';
import 'rxjs/add/operator/take';
-const rootFirebase = 'https://angularfire2-list-factory.firebaseio-demo.com/';
+export const firebaseConfig: FirebaseAppConfig = {
+ apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA",
+ authDomain: "angularfire2-test.firebaseapp.com",
+ databaseURL: "https://angularfire2-test.firebaseio.com/",
+ storageBucket: "angularfire2-test.appspot.com",
+};
+const rootFirebase = firebaseConfig.databaseURL;
function queryTest(observable: Observable, subject: Subject, done: any) {
let nexted = false;
@@ -52,6 +67,17 @@ describe('FirebaseListFactory', () => {
var val1: any;
var val2: any;
var val3: any;
+ var app: firebase.app.App;
+
+ beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(firebaseConfig)]);
+
+ beforeEach(inject([FirebaseApp, AngularFire], (firebaseApp: firebase.app.App, _af: AngularFire) => {
+ app = firebaseApp;
+ }));
+
+ afterEach(done => {
+ app.delete().then(done, done.fail);
+ });
describe('constructor', () => {
@@ -61,14 +87,14 @@ describe('FirebaseListFactory', () => {
});
it('should accept a Firebase db ref in the constructor', () => {
- const list = FirebaseListFactory(new Firebase(`${rootFirebase}/questions`));
+ const list = FirebaseListFactory(firebase.database().refFromURL(`${rootFirebase}/questions`));
expect(list).toBeAnInstanceOf(FirebaseListObservable);
});
});
describe('query', () => {
-
+
describe('orderByChild', () => {
/*
orderByChild combinations
@@ -79,7 +105,7 @@ describe('FirebaseListFactory', () => {
orderByChild("").endAt();
*/
it('equalTo - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -87,12 +113,12 @@ describe('FirebaseListFactory', () => {
equalTo: subject
}
});
-
+
queryTest(observable, subject, done);
});
-
+
it('startAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -100,12 +126,12 @@ describe('FirebaseListFactory', () => {
startAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
+ });
it('endAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -113,12 +139,12 @@ describe('FirebaseListFactory', () => {
endAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
-
+ });
+
it('should throw an error if limitToLast and limitToFirst are chained', () => {
-
+
const observable = FirebaseListFactory(rootFirebase, {
query: {
orderByChild: 'height',
@@ -128,9 +154,9 @@ describe('FirebaseListFactory', () => {
});
expect(observable.subscribe).toThrowError();
});
-
+
it('should throw an error if startAt is used with equalTo', () => {
-
+
const observable = FirebaseListFactory(rootFirebase, {
query: {
orderByChild: 'height',
@@ -140,9 +166,9 @@ describe('FirebaseListFactory', () => {
});
expect(observable.subscribe).toThrowError();
});
-
+
it('should throw an error if endAt is used with equalTo', () => {
-
+
const observable = FirebaseListFactory(rootFirebase, {
query: {
orderByChild: 'height',
@@ -152,9 +178,9 @@ describe('FirebaseListFactory', () => {
});
expect(observable.subscribe).toThrowError();
});
-
+
it('should throw an error if startAt and endAt is used with equalTo', () => {
-
+
const observable = FirebaseListFactory(rootFirebase, {
query: {
orderByChild: 'height',
@@ -164,10 +190,10 @@ describe('FirebaseListFactory', () => {
}
});
expect(observable.subscribe).toThrowError();
- });
-
+ });
+
});
-
+
describe('orderByValue', () => {
/*
orderByValue combinations
@@ -178,7 +204,7 @@ describe('FirebaseListFactory', () => {
orderByValue("").endAt();
*/
it('equalTo - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -186,12 +212,12 @@ describe('FirebaseListFactory', () => {
equalTo: subject
}
});
-
+
queryTest(observable, subject, done);
});
-
+
it('startAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -199,12 +225,12 @@ describe('FirebaseListFactory', () => {
startAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
+ });
it('endAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -212,12 +238,12 @@ describe('FirebaseListFactory', () => {
endAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
-
+ });
+
});
-
+
describe('orderByKey', () => {
/*
orderByKey combinations
@@ -228,7 +254,7 @@ describe('FirebaseListFactory', () => {
orderByKey("").endAt();
*/
it('equalTo - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -236,12 +262,12 @@ describe('FirebaseListFactory', () => {
equalTo: subject
}
});
-
+
queryTest(observable, subject, done);
});
-
+
it('startAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -249,12 +275,12 @@ describe('FirebaseListFactory', () => {
startAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
+ });
it('endAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -262,11 +288,11 @@ describe('FirebaseListFactory', () => {
endAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
+ });
});
-
+
describe('orderByPriority', () => {
/*
orderByPriority combinations
@@ -277,7 +303,7 @@ describe('FirebaseListFactory', () => {
orderByPriority("").endAt();
*/
it('equalTo - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -285,12 +311,12 @@ describe('FirebaseListFactory', () => {
equalTo: subject
}
});
-
+
queryTest(observable, subject, done);
});
-
+
it('startAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -298,12 +324,12 @@ describe('FirebaseListFactory', () => {
startAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
+ });
it('endAt - should re-run a query when the observable value has emitted', (done: any) => {
-
+
const subject = new Subject();
const observable = FirebaseListFactory(rootFirebase, {
query: {
@@ -311,20 +337,20 @@ describe('FirebaseListFactory', () => {
endAt: subject
}
});
-
+
queryTest(observable, subject, done);
- });
- });
-
+ });
+ });
+
});
describe('methods', () => {
beforeEach((done: any) => {
- val1 = { key: () => 'key1' };
- val2 = { key: () => 'key2' };
- val3 = { key: () => 'key3' };
- (new Firebase(rootFirebase)).remove(done);
+ val1 = { key: 'key1' };
+ val2 = { key: 'key2' };
+ val3 = { key: 'key3' };
+ firebase.database().ref().remove(done);
questions = FirebaseListFactory(`${rootFirebase}/questions`);
questionsSnapshotted = FirebaseListFactory(`${rootFirebase}/questionssnapshot`, { preserveSnapshot: true });
ref = (questions)._ref;
@@ -340,7 +366,6 @@ describe('FirebaseListFactory', () => {
it('should emit only when the initial data set has been loaded', (done: any) => {
-
(questions)._ref.set([{ initial1: true }, { initial2: true }, { initial3: true }, { initial4: true }])
.then(() => questions.take(1).toPromise())
.then((val: any[]) => {
@@ -405,8 +430,9 @@ describe('FirebaseListFactory', () => {
it('should call off on all events when disposed', () => {
- var firebaseSpy = spyOn(Firebase.prototype, 'off').and.callThrough();
- subscription = FirebaseListFactory(rootFirebase).subscribe();
+ const questionRef = firebase.database().ref().child('questions');
+ var firebaseSpy = spyOn(questionRef, 'off').and.callThrough();
+ subscription = FirebaseListFactory(questionRef).subscribe();
expect(firebaseSpy).not.toHaveBeenCalled();
subscription.unsubscribe();
expect(firebaseSpy).toHaveBeenCalled();
@@ -452,9 +478,7 @@ describe('FirebaseListFactory', () => {
it('should update the child', () => {
expect(
- onChildUpdated([val1, val2, val3], {
- key: () => 'newkey'
- }, 'key1').map(v => v.key())
+ onChildUpdated([val1, val2, val3], { key: 'newkey' }, 'key1').map(v => v.key)
).toEqual(['key1', 'newkey', 'key3']);
});
});
@@ -483,12 +507,13 @@ describe('FirebaseListFactory', () => {
describe('utils.unwrapMapFn', () => {
var val = { unwrapped: true };
var snapshot = {
- key: () => 'key',
+ ref: { key: 'key' },
val: () => val
};
it('should return an object value with a $key property', () => {
- expect(utils.unwrapMapFn(snapshot as FirebaseDataSnapshot)).toEqual({
+ const unwrapped = utils.unwrapMapFn(snapshot as firebase.database.DataSnapshot);
+ expect(unwrapped).toEqual({
$key: 'key',
unwrapped: true
});
@@ -496,15 +521,15 @@ describe('FirebaseListFactory', () => {
it('should return an object value with a $value property if value is scalar', () => {
- expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 5 }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 5 }) as firebase.database.DataSnapshot)).toEqual({
$key: 'key',
$value: 5
});
- expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => false }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => false }) as firebase.database.DataSnapshot)).toEqual({
$key: 'key',
$value: false
});
- expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 'lol' }) as FirebaseDataSnapshot)).toEqual({
+ expect(utils.unwrapMapFn(Object.assign(snapshot, { val: () => 'lol' }) as firebase.database.DataSnapshot)).toEqual({
$key: 'key',
$value: 'lol'
});
diff --git a/src/utils/firebase_list_factory.ts b/src/utils/firebase_list_factory.ts
index de9ff2b0d..4708b28b1 100644
--- a/src/utils/firebase_list_factory.ts
+++ b/src/utils/firebase_list_factory.ts
@@ -1,31 +1,31 @@
import {FirebaseListObservable, AFUnwrappedDataSnapshot} from './firebase_list_observable';
import {Observer} from 'rxjs/Observer';
-import * as Firebase from 'firebase';
+import { database } from 'firebase';
import * as utils from './utils';
import {Query, observeQuery} from './query_observable';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/map';
-export function FirebaseListFactory (absoluteUrlOrDbRef:string | Firebase | FirebaseQuery, {preserveSnapshot, query = {}}:FirebaseListFactoryOpts = {}): FirebaseListObservable {
- let ref: Firebase | FirebaseQuery;
-
+export function FirebaseListFactory (absoluteUrlOrDbRef:string | firebase.database.Reference | firebase.database.Query, {preserveSnapshot, query = {}}:FirebaseListFactoryOpts = {}): FirebaseListObservable {
+ let ref: firebase.database.Reference | firebase.database.Query;
+
utils.checkForUrlOrFirebaseRef(absoluteUrlOrDbRef, {
- isUrl: () => ref = new Firebase(absoluteUrlOrDbRef),
- isRef: () => ref = absoluteUrlOrDbRef,
- isQuery: () => ref = absoluteUrlOrDbRef,
+ isUrl: () => ref = database().refFromURL(absoluteUrlOrDbRef),
+ isRef: () => ref = absoluteUrlOrDbRef,
+ isQuery: () => ref = absoluteUrlOrDbRef,
});
-
+
// if it's just a reference or string, create a regular list observable
- if ((utils.isFirebaseRef(absoluteUrlOrDbRef) ||
- utils.isString(absoluteUrlOrDbRef)) &&
+ if ((utils.isFirebaseRef(absoluteUrlOrDbRef) ||
+ utils.isString(absoluteUrlOrDbRef)) &&
utils.isEmptyObject(query)) {
return firebaseListObservable(ref, { preserveSnapshot });
}
-
+
const queryObs = observeQuery(query);
const listObs = >queryObs
.map(query => {
- let queried: FirebaseQuery = ref;
+ let queried: firebase.database.Query = ref;
// Only apply the populated keys
// apply ordering and available querying options
// eg: ref.orderByChild('height').startAt(3)
@@ -39,59 +39,59 @@ export function FirebaseListFactory (absoluteUrlOrDbRef:string | Firebase | Fire
} else if (query.orderByValue) {
queried = queried.orderByValue();
}
-
+
// check equalTo
if (utils.isPresent(query.equalTo)) {
queried = queried.equalTo(query.equalTo);
-
+
if (utils.isPresent(query.startAt) || query.endAt) {
throw new Error('Query Error: Cannot use startAt or endAt with equalTo.');
- }
-
+ }
+
// apply limitTos
if (utils.isPresent(query.limitToFirst)) {
queried = queried.limitToFirst(query.limitToFirst);
}
-
+
if (utils.isPresent(query.limitToLast)) {
queried = queried.limitToLast(query.limitToLast);
}
-
+
return queried;
}
-
+
// check startAt
if (utils.isPresent(query.startAt)) {
queried = queried.startAt(query.startAt);
}
-
+
if (utils.isPresent(query.endAt)) {
queried = queried.endAt(query.endAt);
}
-
+
if (utils.isPresent(query.limitToFirst) && query.limitToLast) {
throw new Error('Query Error: Cannot use limitToFirst with limitToLast.');
}
-
+
// apply limitTos
if (utils.isPresent(query.limitToFirst)) {
queried = queried.limitToFirst(query.limitToFirst);
}
-
+
if (utils.isPresent(query.limitToLast)) {
queried = queried.limitToLast(query.limitToLast);
}
-
+
return queried;
})
- .mergeMap((queryRef: Firebase, ix: number) => {
+ .mergeMap((queryRef: firebase.database.Reference, ix: number) => {
return firebaseListObservable(queryRef, { preserveSnapshot });
});
return listObs;
}
-function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable {
-
+function firebaseListObservable(ref: firebase.database.Reference | firebase.database.Query, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable {
+
const listObs = new FirebaseListObservable(ref, (obs: Observer) => {
let arr: any[] = [];
let hasInitialLoad = false;
@@ -103,8 +103,9 @@ function firebaseListObservable(ref: Firebase | FirebaseQuery, {preserveSnapshot
ref.once('value', (snap) => {
hasInitialLoad = true;
obs.next(preserveSnapshot ? arr : arr.map(utils.unwrapMapFn));
- }, err => {
- if (err) { obs.error(err); obs.complete(); }
+ }).catch(err => {
+ obs.error(err);
+ obs.complete()
});
ref.on('child_added', (child: any, prevKey: string) => {
@@ -151,12 +152,12 @@ export function onChildAdded(arr:any[], child:any, prevKey:string): any[] {
return [child];
}
- return arr.reduce((accumulator:FirebaseDataSnapshot[], curr:FirebaseDataSnapshot, i:number) => {
+ return arr.reduce((accumulator:firebase.database.DataSnapshot[], curr:firebase.database.DataSnapshot, i:number) => {
if (!prevKey && i===0) {
accumulator.push(child);
}
accumulator.push(curr);
- if (prevKey && prevKey === curr.key()) {
+ if (prevKey && prevKey === curr.key) {
accumulator.push(child);
}
return accumulator;
@@ -167,13 +168,13 @@ export function onChildChanged(arr:any[], child:any, prevKey:string): any[] {
return arr.reduce((accumulator:any[], val:any, i:number) => {
if (!prevKey && i==0) {
accumulator.push(child);
- if (val.key() !== child.key()) {
+ if (val.key !== child.key) {
accumulator.push(val);
}
- } else if(val.key() === prevKey) {
+ } else if(val.key === prevKey) {
accumulator.push(val);
accumulator.push(child);
- } else if (val.key() !== child.key()) {
+ } else if (val.key !== child.key) {
accumulator.push(val);
}
return accumulator;
@@ -181,14 +182,14 @@ export function onChildChanged(arr:any[], child:any, prevKey:string): any[] {
}
export function onChildRemoved(arr:any[], child:any): any[] {
- return arr.filter(c => c.key() !== child.key());
+ return arr.filter(c => c.key !== child.key);
}
export function onChildUpdated(arr:any[], child:any, prevKey:string): any[] {
return arr.map((v, i, arr) => {
if(!prevKey && !i) {
return child;
- } else if (i > 0 && arr[i-1].key() === prevKey) {
+ } else if (i > 0 && arr[i-1].key === prevKey) {
return child;
} else {
return v;
diff --git a/src/utils/firebase_list_observable.spec.ts b/src/utils/firebase_list_observable.spec.ts
index cab388ac3..9082260ae 100644
--- a/src/utils/firebase_list_observable.spec.ts
+++ b/src/utils/firebase_list_observable.spec.ts
@@ -1,39 +1,56 @@
-import {describe,it,iit,beforeEach} from '@angular/core/testing';
+import {describe,ddescribe,it,iit,beforeEach,beforeEachProviders,inject} from '@angular/core/testing';
import {FirebaseListObservable} from './firebase_list_observable';
+import {
+ FIREBASE_PROVIDERS,
+ defaultFirebase,
+ FirebaseApp,
+ FirebaseAppConfig,
+ AngularFire
+} from '../angularfire2';
import {Observer} from 'rxjs/Observer';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
-import * as Firebase from 'firebase';
+import { database } from 'firebase';
import {unwrapMapFn} from './utils';
-const rootUrl = 'https://angularfire2-list-obs.firebaseio-demo.com/';
+export const firebaseConfig: FirebaseAppConfig = {
+ apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA",
+ authDomain: "angularfire2-test.firebaseapp.com",
+ databaseURL: "https://angularfire2-test.firebaseio.com/",
+ storageBucket: "angularfire2-test.appspot.com",
+};
+const rootUrl = firebaseConfig.databaseURL;
describe('FirebaseObservable', () => {
var O:FirebaseListObservable;
- var ref:Firebase;
+ var ref:firebase.database.Reference;
+ var app: firebase.app.App;
- beforeEach(() => {
- ref = new Firebase(rootUrl);
+ beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(firebaseConfig)]);
+
+ beforeEach(inject([FirebaseApp, AngularFire], (firebaseApp: firebase.app.App, _af: AngularFire) => {
+ app = firebaseApp;
+ ref = database().ref();
O = new FirebaseListObservable(ref, (observer:Observer) => {
});
- });
+ }));
- afterEach((done:any) => {
+ afterEach(done => {
+ app.delete().then(done, done.fail);
ref.off();
ref.remove(done);
});
-
it('should return an instance of FirebaseObservable when calling operators', () => {
O = new FirebaseListObservable(ref, (observer:Observer) => {
});
expect(O.map(noop) instanceof FirebaseListObservable).toBe(true);
});
-
+
describe('push', () => {
it('should throw an exception if pushed when not subscribed', () => {
O = new FirebaseListObservable(null, (observer:Observer) => {});
-
+
expect(() => {
O.push('foo');
}).toThrowError('No ref specified for this Observable!')
@@ -47,7 +64,7 @@ describe('FirebaseObservable', () => {
describe('remove', () => {
var orphan = { orphan: true };
- var child:Firebase;
+ var child:firebase.database.Reference;
beforeEach(() => {
child = ref.push(orphan);
@@ -58,9 +75,9 @@ describe('FirebaseObservable', () => {
var childAddedSpy = jasmine.createSpy('childAdded');
ref.on('child_added', childAddedSpy);
- O.remove(child.key())
+ O.remove(child.key)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
+ .then((data:firebase.database.DataSnapshot) => {
expect(childAddedSpy.calls.argsFor(0)[0].val()).toEqual(orphan);
expect(data.val()).toBeNull();
ref.off();
@@ -76,7 +93,7 @@ describe('FirebaseObservable', () => {
O.remove(child)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
+ .then((data:firebase.database.DataSnapshot) => {
expect(childAddedSpy.calls.argsFor(0)[0].val()).toEqual(orphan);
expect(data.val()).toBeNull();
ref.off();
@@ -86,11 +103,11 @@ describe('FirebaseObservable', () => {
it('should remove the item from the Firebase db when given the snapshot', (done:any) => {
- ref.on('child_added', (data:FirebaseDataSnapshot) => {
+ ref.on('child_added', (data:firebase.database.DataSnapshot) => {
expect(data.val()).toEqual(orphan);
O.remove(data)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
+ .then((data:firebase.database.DataSnapshot) => {
expect(data.val()).toBeNull();
ref.off();
})
@@ -100,22 +117,22 @@ describe('FirebaseObservable', () => {
it('should remove the item from the Firebase db when given the unwrapped snapshot', (done:any) => {
- ref.on('child_added', (data:FirebaseDataSnapshot) => {
+ ref.on('child_added', (data:firebase.database.DataSnapshot) => {
expect(data.val()).toEqual(orphan);
O.remove(unwrapMapFn(data))
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
+ .then((data:firebase.database.DataSnapshot) => {
expect(data.val()).toBeNull();
ref.off();
})
.then(done, done.fail);
});
});
-
+
it('should remove the whole list if no item is added', () => {
O.remove()
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
+ .then((data:firebase.database.DataSnapshot) => {
expect(data.val()).toBe(null);
});
});
@@ -126,48 +143,48 @@ describe('FirebaseObservable', () => {
expect(() => O.remove(input)).toThrowError(`FirebaseListObservable.remove requires a key, snapshot, reference, or unwrapped snapshot. Got: ${typeof input}`);
})
});
-
+
describe('update', () => {
var orphan = { orphan: true };
- var child:Firebase;
+ var child:firebase.database.Reference;
beforeEach(() => {
child = ref.push(orphan);
- });
-
+ });
+
it('should update the item from the Firebase db when given the key', (done:any) => {
var childChangedSpy = jasmine.createSpy('childChanged');
const orphanChange = { changed: true }
ref.on('child_changed', childChangedSpy);
- O.update(child.key(), orphanChange)
+ O.update(child.key, orphanChange)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
- expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
+ .then((data:firebase.database.DataSnapshot) => {
+ expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
orphan: true,
changed: true
});
-
+
ref.off();
})
.then(done, done.fail);
});
-
+
it('should update the item from the Firebase db when given the reference', (done:any) => {
var childChangedSpy = jasmine.createSpy('childChanged');
const orphanChange = { changed: true }
ref.on('child_changed', childChangedSpy);
- O.update(child.ref(), orphanChange)
+ O.update(child.ref, orphanChange)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
- expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
+ .then((data:firebase.database.DataSnapshot) => {
+ expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
orphan: true,
changed: true
});
-
+
ref.off();
})
.then(done, done.fail);
- });
+ });
it('should update the item from the Firebase db when given the snapshot', (done:any) => {
var childChangedSpy = jasmine.createSpy('childChanged');
@@ -175,12 +192,12 @@ describe('FirebaseObservable', () => {
ref.on('child_changed', childChangedSpy);
O.update(child, orphanChange)
.then(() => (ref).once('value'))
- .then((data:FirebaseDataSnapshot) => {
- expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
+ .then((data:firebase.database.DataSnapshot) => {
+ expect(childChangedSpy.calls.argsFor(0)[0].val()).toEqual({
orphan: true,
changed: true
});
-
+
ref.off();
})
.then(done, done.fail);
@@ -188,12 +205,12 @@ describe('FirebaseObservable', () => {
it('should update the item from the Firebase db when given the unwrapped snapshot', (done:any) => {
const orphanChange = { changed: true }
- ref.on('child_added', (data:FirebaseDataSnapshot) => {
+ ref.on('child_added', (data:firebase.database.DataSnapshot) => {
expect(data.val()).toEqual(orphan);
O.update(unwrapMapFn(data), orphanChange)
.then(() => (child).once('value'))
- .then((data:FirebaseDataSnapshot) => {
- expect(data.val()).toEqual({
+ .then((data:firebase.database.DataSnapshot) => {
+ expect(data.val()).toEqual({
orphan: true,
changed: true
});
@@ -201,10 +218,10 @@ describe('FirebaseObservable', () => {
})
.then(done, done.fail);
});
- });
-
+ });
+
});
-
+
});
function noop() {}
diff --git a/src/utils/firebase_list_observable.ts b/src/utils/firebase_list_observable.ts
index 378137266..c73020131 100644
--- a/src/utils/firebase_list_observable.ts
+++ b/src/utils/firebase_list_observable.ts
@@ -4,17 +4,22 @@ import {Subscriber} from 'rxjs/Subscriber';
import {Subscription} from 'rxjs/Subscription';
import * as utils from './utils';
-export type FirebaseOperation = string | Firebase | FirebaseDataSnapshot | AFUnwrappedDataSnapshot
-
export interface FirebaseOperationCases {
- stringCase: () => Promise;
- firebaseCase?: () => Promise;
- snapshotCase?: () => Promise;
- unwrappedSnapshotCase?: () => Promise;
+ stringCase: () => firebase.Promise;
+ firebaseCase?: () => firebase.Promise;
+ snapshotCase?: () => firebase.Promise;
+ unwrappedSnapshotCase?: () => firebase.Promise;
+}
+
+export interface AFUnwrappedDataSnapshot {
+ $key: string;
+ $value?: string | number | boolean;
}
+export type FirebaseOperation = string | firebase.database.Reference | firebase.database.DataSnapshot | AFUnwrappedDataSnapshot;
+
export class FirebaseListObservable extends Observable {
- constructor(public _ref: Firebase | FirebaseQuery, subscribe?: (subscriber: Subscriber) => Subscription | Function | void) {
+ constructor(public _ref: firebase.database.Reference | firebase.database.Query, subscribe?: (subscriber: Subscriber) => Subscription | Function | void) {
super(subscribe);
}
lift(operator: Operator): Observable {
@@ -23,41 +28,42 @@ export class FirebaseListObservable extends Observable {
observable.operator = operator;
observable._ref = this._ref;
return observable;
- }
+ }
- push(val:any):FirebaseWithPromise {
+ push(val:any):firebase.database.ThenableReference {
if(!this._ref) {
throw new Error('No ref specified for this Observable!');
}
- return this._ref.ref().push(val);
+ this._ref.ref
+ return this._ref.ref.push(val);
}
-
- update(item: FirebaseOperation, value: Object): Promise {
+
+ update(item: FirebaseOperation, value: Object): firebase.Promise {
return this._checkOperationCases(item, {
- stringCase: () => this._ref.ref().child(item).update(value),
- firebaseCase: () => (item).update(value),
- snapshotCase: () => (item).ref().update(value),
- unwrappedSnapshotCase: () => this._ref.ref().child((item).$key).update(value)
+ stringCase: () => this._ref.ref.child(item).update(value),
+ firebaseCase: () => (item).update(value),
+ snapshotCase: () => (item).ref.update(value),
+ unwrappedSnapshotCase: () => this._ref.ref.child((item).$key).update(value)
});
}
- remove(item:FirebaseOperation = null): Promise {
+ remove(item:FirebaseOperation = null): firebase.Promise {
// TODO: remove override when typings are updated to include
// remove() returning a promise.
-
+
// if no item parameter is provided, remove the whole list
if (!item) {
- return this._ref.ref().remove();
+ return this._ref.ref.remove();
}
return this._checkOperationCases(item, {
- stringCase: () => this._ref.ref().child(item).remove(),
- firebaseCase: () => (item).remove(),
- snapshotCase: () => (item).ref().remove(),
- unwrappedSnapshotCase: () => this._ref.ref().child((item).$key).remove()
+ stringCase: () => this._ref.ref.child(item).remove(),
+ firebaseCase: () => (item).remove(),
+ snapshotCase: () => (item).ref.remove(),
+ unwrappedSnapshotCase: () => this._ref.ref.child((item).$key).remove()
});
}
-
- _checkOperationCases(item: FirebaseOperation, cases: FirebaseOperationCases) : Promise {
+
+ _checkOperationCases(item: FirebaseOperation, cases: FirebaseOperationCases) : firebase.Promise {
if (utils.isString(item)) {
return cases.stringCase();
} else if (utils.isFirebaseRef(item)) {
@@ -72,10 +78,5 @@ export class FirebaseListObservable extends Observable {
}
throw new Error(`FirebaseListObservable.remove requires a key, snapshot, reference, or unwrapped snapshot. Got: ${typeof item}`);
}
-
-}
-export interface AFUnwrappedDataSnapshot {
- $key: string;
- $value?: string | number | boolean;
}
diff --git a/src/utils/firebase_object_factory.spec.ts b/src/utils/firebase_object_factory.spec.ts
index acfb6a99c..7dc45f565 100644
--- a/src/utils/firebase_object_factory.spec.ts
+++ b/src/utils/firebase_object_factory.spec.ts
@@ -1,16 +1,49 @@
import {FirebaseObjectFactory} from '../utils/firebase_object_factory';
import {FirebaseObjectObservable} from '../utils/firebase_object_observable';
-import {beforeEach, it, iit, describe, expect} from '@angular/core/testing';
+import {
+ beforeEach,
+ it,
+ iit,
+ ddescribe,
+ describe,
+ expect,
+ beforeEachProviders,
+ inject
+} from '@angular/core/testing';
+import {
+ FIREBASE_PROVIDERS,
+ defaultFirebase,
+ FirebaseApp,
+ FirebaseAppConfig,
+ AngularFire
+} from '../angularfire2';
import {Subscription} from 'rxjs';
-const rootFirebase = 'https://angularfire2-object-factory.firebaseio-demo.com/';
+export const firebaseConfig: FirebaseAppConfig = {
+ apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA",
+ authDomain: "angularfire2-test.firebaseapp.com",
+ databaseURL: "https://angularfire2-test.firebaseio.com/",
+ storageBucket: "angularfire2-test.appspot.com",
+};
+const rootFirebase = firebaseConfig.databaseURL;
describe('FirebaseObjectFactory', () => {
var i = 0;
- var ref: Firebase;
+ var ref: firebase.database.Reference;
var observable: FirebaseObjectObservable;
var subscription: Subscription;
var nextSpy: jasmine.Spy;
+ var app: firebase.app.App;
+
+ beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(firebaseConfig)]);
+
+ beforeEach(inject([FirebaseApp, AngularFire], (firebaseApp: firebase.app.App, _af: AngularFire) => {
+ app = firebaseApp;
+ }));
+
+ afterEach(done => {
+ app.delete().then(done, done.fail);
+ });
describe('constructor', () => {
@@ -20,7 +53,7 @@ describe('FirebaseObjectFactory', () => {
});
it('should accept a Firebase db ref in the constructor', () => {
- const object = FirebaseObjectFactory(new Firebase(`${rootFirebase}/questions`));
+ const object = FirebaseObjectFactory(firebase.database().ref().child(`questions`));
expect(object).toBeAnInstanceOf(FirebaseObjectObservable);
});
@@ -30,7 +63,7 @@ describe('FirebaseObjectFactory', () => {
beforeEach((done: any) => {
i = Date.now();
- ref = new Firebase(`${rootFirebase}/questions/${i}`);
+ ref = firebase.database().ref().child(`questions/${i}`);
nextSpy = nextSpy = jasmine.createSpy('next');
observable = FirebaseObjectFactory(`${rootFirebase}/questions/${i}`);
ref.remove(done);
@@ -45,7 +78,7 @@ describe('FirebaseObjectFactory', () => {
it('should emit a null value if no value is present when subscribed', (done: any) => {
subscription = observable.subscribe(val => {
- expect(val).toEqual({ $key: (observable)._ref.key(), $value: null });
+ expect(val).toEqual({ $key: (observable)._ref.key, $value: null });
done();
});
});
@@ -55,21 +88,21 @@ describe('FirebaseObjectFactory', () => {
ref.set({ unwrapped: 'bar' }, () => {
subscription = observable.subscribe(val => {
if (!val) return;
- expect(val).toEqual({ $key: ref.key(), unwrapped: 'bar' });
+ expect(val).toEqual({ $key: ref.key, unwrapped: 'bar' });
done();
});
});
});
-
+
it('should emit unwrapped data with a $value property for primitive values', (done: any) => {
ref.set('fiiiireeee', () => {
subscription = observable.subscribe(val => {
if (!val) return;
- expect(val).toEqual({ $key: ref.key(), $value: 'fiiiireeee' });
+ expect(val).toEqual({ $key: ref.key, $value: 'fiiiireeee' });
done();
});
});
- });
+ });
it('should emit snapshots if preserveSnapshot option is true', (done: any) => {
@@ -86,8 +119,9 @@ describe('FirebaseObjectFactory', () => {
it('should call off on all events when disposed', () => {
- var firebaseSpy = spyOn(Firebase.prototype, 'off');
- subscription = FirebaseObjectFactory(rootFirebase).subscribe();
+ const dbRef = firebase.database().ref();
+ var firebaseSpy = spyOn(dbRef, 'off');
+ subscription = FirebaseObjectFactory(dbRef).subscribe();
expect(firebaseSpy).not.toHaveBeenCalled();
subscription.unsubscribe();
expect(firebaseSpy).toHaveBeenCalled();
diff --git a/src/utils/firebase_object_factory.ts b/src/utils/firebase_object_factory.ts
index 68a7ed1e5..6c3d2147b 100644
--- a/src/utils/firebase_object_factory.ts
+++ b/src/utils/firebase_object_factory.ts
@@ -1,20 +1,20 @@
import {FirebaseObjectObservable} from './firebase_object_observable';
import {Observer} from 'rxjs/Observer';
import 'rxjs/add/operator/mergeMap';
-import * as Firebase from 'firebase';
+import { database } from 'firebase';
import * as utils from './utils';
import {Query, observeQuery} from './query_observable';
-export function FirebaseObjectFactory(absoluteUrlOrDbRef: string | Firebase, {preserveSnapshot, query}: FirebaseObjectFactoryOpts = {}): FirebaseObjectObservable {
- let ref: Firebase;
+export function FirebaseObjectFactory(absoluteUrlOrDbRef: string | firebase.database.Reference, {preserveSnapshot, query}: FirebaseObjectFactoryOpts = {}): FirebaseObjectObservable {
+ let ref: firebase.database.Reference;
utils.checkForUrlOrFirebaseRef(absoluteUrlOrDbRef, {
- isUrl: () => ref = new Firebase(absoluteUrlOrDbRef),
- isRef: () => ref = absoluteUrlOrDbRef
+ isUrl: () => ref = database().refFromURL(absoluteUrlOrDbRef),
+ isRef: () => ref = absoluteUrlOrDbRef
});
-
+
return new FirebaseObjectObservable((obs: Observer) => {
- ref.on('value', (snapshot: FirebaseDataSnapshot) => {
+ ref.on('value', (snapshot: firebase.database.DataSnapshot) => {
obs.next(preserveSnapshot ? snapshot : utils.unwrapMapFn(snapshot))
}, err => {
if (err) { obs.error(err); obs.complete(); }
diff --git a/src/utils/firebase_object_observable.spec.ts b/src/utils/firebase_object_observable.spec.ts
index b96d9fdca..3500de2f6 100644
--- a/src/utils/firebase_object_observable.spec.ts
+++ b/src/utils/firebase_object_observable.spec.ts
@@ -1,35 +1,61 @@
-import {describe,it,beforeEach} from '@angular/core/testing';
+import {
+ beforeEach,
+ it,
+ iit,
+ ddescribe,
+ describe,
+ expect,
+ beforeEachProviders,
+ inject
+} from '@angular/core/testing';
+import {
+ FIREBASE_PROVIDERS,
+ defaultFirebase,
+ FirebaseApp,
+ FirebaseAppConfig,
+ AngularFire
+} from '../angularfire2';
import {FirebaseObjectObservable} from './firebase_object_observable';
import {Observer} from 'rxjs/Observer';
import 'rxjs/add/operator/map';
-import * as Firebase from 'firebase';
+import { database } from 'firebase';
-const rootUrl = 'https://angularfire2-obj-obs.firebaseio-demo.com/';
+export const firebaseConfig: FirebaseAppConfig = {
+ apiKey: "AIzaSyBVSy3YpkVGiKXbbxeK0qBnu3-MNZ9UIjA",
+ authDomain: "angularfire2-test.firebaseapp.com",
+ databaseURL: "https://angularfire2-test.firebaseio.com/",
+ storageBucket: "angularfire2-test.appspot.com",
+};
+const rootUrl = firebaseConfig.databaseURL;
describe('FirebaseObjectObservable', () => {
-
+
var O:FirebaseObjectObservable;
- var ref:Firebase;
+ var ref: firebase.database.Reference;
+ var app: firebase.app.App;
+
+ beforeEachProviders(() => [FIREBASE_PROVIDERS, defaultFirebase(firebaseConfig)]);
- beforeEach(() => {
- ref = new Firebase(rootUrl);
+ beforeEach(inject([FirebaseApp, AngularFire], (firebaseApp: firebase.app.App, _af: AngularFire) => {
+ app = firebaseApp;
+ ref = database().ref()
O = new FirebaseObjectObservable((observer:Observer) => {
}, ref);
- });
+ }));
- afterEach((done:any) => {
+ afterEach(done => {
ref.off();
ref.remove();
- done();
+ app.delete().then(done, done.fail);
});
-
+
it('should return an instance of FirebaseObservable when calling operators', () => {
var O = new FirebaseObjectObservable((observer:Observer) => {});
expect(O.map(noop) instanceof FirebaseObjectObservable).toBe(true);
});
-
+
describe('set', () => {
-
+
it('should call set on the underlying ref', (done:any) => {
var setSpy = spyOn(ref, 'set');
@@ -37,8 +63,8 @@ describe('FirebaseObjectObservable', () => {
O.set(1);
expect(setSpy).toHaveBeenCalledWith(1);
done();
- });
-
+ });
+
it('should throw an exception if set when not subscribed', () => {
O = new FirebaseObjectObservable((observer:Observer) => {});
@@ -46,7 +72,7 @@ describe('FirebaseObjectObservable', () => {
O.set('foo');
}).toThrowError('No ref specified for this Observable!')
});
-
+
it('should accept any type of value without compilation error', () => {
O.set('foo');
});
@@ -54,10 +80,10 @@ describe('FirebaseObjectObservable', () => {
it('should resolve returned thenable when successful', (done:any) => {
O.set('foo').then(done, done.fail);
- });
-
+ });
+
});
-
+
describe('update', () => {
const updateObject = { hot: 'firebae' };
it('should call update on the underlying ref', () => {
@@ -66,8 +92,8 @@ describe('FirebaseObjectObservable', () => {
O.subscribe();
O.update(updateObject);
expect(updateSpy).toHaveBeenCalledWith(updateObject);
- });
-
+ });
+
it('should throw an exception if updated when not subscribed', () => {
O = new FirebaseObjectObservable((observer:Observer) => {});
@@ -75,16 +101,16 @@ describe('FirebaseObjectObservable', () => {
O.update(updateObject);
}).toThrowError('No ref specified for this Observable!')
});
-
+
it('should accept any type of value without compilation error', () => {
O.update(updateObject);
});
it('should resolve returned thenable when successful', (done:any) => {
O.update(updateObject).then(done, done.fail);
- });
-
- });
+ });
+
+ });
describe('remove', () => {
@@ -94,8 +120,8 @@ describe('FirebaseObjectObservable', () => {
O.subscribe();
O.remove();
expect(removeSpy).toHaveBeenCalledWith();
- });
-
+ });
+
it('should throw an exception if removed when not subscribed', () => {
O = new FirebaseObjectObservable((observer:Observer) => {});
@@ -103,13 +129,13 @@ describe('FirebaseObjectObservable', () => {
O.remove();
}).toThrowError('No ref specified for this Observable!')
});
-
+
it('should resolve returned thenable when successful', (done:any) => {
O.remove().then(done, done.fail);
- });
-
- });
-
+ });
+
+ });
+
});
function noop() {}
diff --git a/src/utils/firebase_object_observable.ts b/src/utils/firebase_object_observable.ts
index 1b99c1558..31bf0e457 100644
--- a/src/utils/firebase_object_observable.ts
+++ b/src/utils/firebase_object_observable.ts
@@ -4,7 +4,7 @@ import {Subscriber} from 'rxjs/Subscriber';
import {Subscription} from 'rxjs/Subscription';
export class FirebaseObjectObservable extends Observable {
- constructor(subscribe?: (subscriber: Subscriber) => Subscription | Function | void, private _ref?:Firebase) {
+ constructor(subscribe?: (subscriber: Subscriber) => Subscription | Function | void, private _ref?:firebase.database.Reference) {
super(subscribe);
}
lift(operator: Operator): Observable {
@@ -14,19 +14,19 @@ export class FirebaseObjectObservable extends Observable {
observable._ref = this._ref;
return observable;
}
- set(value: any): Promise {
+ set(value: any): firebase.Promise {
if(!this._ref) {
throw new Error('No ref specified for this Observable!');
}
return this._ref.set(value);
}
- update(value: Object): Promise {
+ update(value: Object): firebase.Promise {
if(!this._ref) {
throw new Error('No ref specified for this Observable!');
}
return this._ref.update(value);
}
- remove(): Promise {
+ remove(): firebase.Promise {
if(!this._ref) {
throw new Error('No ref specified for this Observable!');
}
From 5b7ec28fb925e3aeaecc6138b8020d990caa4635 Mon Sep 17 00:00:00 2001
From: Jeff Cross
Date: Mon, 13 Jun 2016 16:05:18 -0700
Subject: [PATCH 021/873] fix(auth): make auth API compatible with new Firebase
SDK
---
src/providers/auth.spec.ts | 612 +++++++++++----------
src/providers/auth.ts | 109 ++--
src/providers/auth_backend.spec.ts | 106 ++++
src/providers/auth_backend.ts | 110 ++--
src/providers/firebase_sdk_auth_backend.ts | 156 +++---
src/providers/web_workers/ui/auth.ts | 1 -
src/providers/web_workers/worker/auth.ts | 24 +-
test/e2e/auth/firebase_auth_example.ts | 2 +-
tsconfig.json | 3 +-
9 files changed, 634 insertions(+), 489 deletions(-)
create mode 100644 src/providers/auth_backend.spec.ts
diff --git a/src/providers/auth.spec.ts b/src/providers/auth.spec.ts
index c4103fb7a..ca290aa46 100644
--- a/src/providers/auth.spec.ts
+++ b/src/providers/auth.spec.ts
@@ -1,158 +1,193 @@
-///
-
-import {expect, describe, it, iit, beforeEach} from '@angular/core/testing';
+import { auth, initializeApp } from 'firebase';
+import {
+ beforeEachProviders,
+ expect,
+ ddescribe,
+ describe,
+ inject,
+ it,
+ iit,
+ beforeEach
+} from '@angular/core/testing';
import {ReflectiveInjector, provide, Provider} from '@angular/core';
-import {Observable} from 'rxjs/Observable'
+import { Observable } from 'rxjs/Observable'
+import { Observer } from 'rxjs/Observer';
+import 'rxjs/add/operator/do';
+
import {
+ defaultFirebase,
FIREBASE_PROVIDERS,
- FirebaseRef,
- FirebaseUrl,
- FirebaseAuth,
- AuthMethods,
+ FirebaseApp,
+ FirebaseAppConfig,
FirebaseAuthState,
+ FirebaseConfig,
+ AngularFireAuth,
+ AuthMethods,
firebaseAuthConfig,
AuthProviders
} from '../angularfire2';
+import { COMMON_CONFIG } from '../test-config';
+
import {AuthBackend} from './auth_backend';
import {FirebaseSdkAuthBackend} from './firebase_sdk_auth_backend';
-import * as Firebase from 'firebase';
-import * as mockPromises from 'mock-promises';
-describe('FirebaseAuth', () => {
- let injector: ReflectiveInjector = null;
- let ref: Firebase = null;
- let authData: any = null;
- let authCb: any = null;
- let backend: AuthBackend = null;
-
- const providerMetadata = {
- accessToken: 'accessToken',
- displayName: 'github User',
- username: 'githubUsername',
- id: '12345',
- expires: 0
+// Set providers from firebase so no firebase.auth.GoogleProvider() necessary
+const {
+ GoogleAuthProvider,
+ TwitterAuthProvider,
+ GithubAuthProvider
+} = auth;
+
+const authMethods = [
+ 'getRedirectResult',
+ 'signInWithCustomToken',
+ 'signInAnonymously',
+ 'signInWithEmailAndPassword',
+ 'signInWithPopup',
+ 'signInWithRedirect',
+ 'signInWithCredential',
+ 'signOut',
+ 'onAuthStateChanged',
+ 'createUserWithEmailAndPassword',
+ 'changeEmail',
+ 'removeUser',
+ 'resetPassword'
+];
+
+const firebaseUser = {
+ uid: '12345',
+ providerData: [{
+ 'displayName': 'jeffbcross',
+ // TODO verify this property name
+ providerId: 'github.com'
+ }]
+};
+
+const githubCredential = {
+ credential: {
+ accessToken: 'ACCESS_TOKEN',
+ provider: 'github.com'
+ },
+ user: firebaseUser
+};
+
+const googleCredential = {
+ credential: {},
+ user: firebaseUser
+}
+
+const AngularFireAuthState = {
+ provider: 0,
+ auth: firebaseUser,
+ uid: '12345',
+ github: {
+ accessToken: 'GH_ACCESS_TOKEN',
+ provider: 'github.com'
}
+};
- const authObj = {
- token: 'key'
- }
-
- const authState = {
- provider: 'github',
- uid: 'github:12345',
- github: providerMetadata,
- auth: authObj,
- expires: 0
- };
-
- const AngularFireAuthState = {
- provider: AuthProviders.Github,
- uid: 'github:12345',
- github: providerMetadata,
- auth: authObj,
- expires: 0
- }
+describe('FirebaseAuth', () => {
+ let app: firebase.app.App;
+ let authData: any;
+ let authCb: any;
+ let backend: AuthBackend;
+ let afAuth: AngularFireAuth;
+ let authSpy: jasmine.Spy;
+ var fbAuthObserver: Observer;
+
+ beforeEachProviders(() => [
+ FIREBASE_PROVIDERS,
+ defaultFirebase(COMMON_CONFIG),
+ provide(FirebaseApp, {
+ useFactory: (config: FirebaseAppConfig) => {
+ var app = initializeApp(config);
+ (app).auth = () => authSpy;
+ return app;
+ },
+ deps: [FirebaseConfig]
+ })
+ ]);
beforeEach(() => {
- authData = null;
- authCb = null;
- injector = ReflectiveInjector.resolveAndCreate([
- provide(FirebaseUrl, {
- useValue: 'https://angularfire2-auth.firebaseio-demo.com/'
- }),
- FIREBASE_PROVIDERS
- ]);
+ authSpy = jasmine.createSpyObj('auth', authMethods);
+ authSpy['createUserWithEmailAndPassword'].and.returnValue(Promise.resolve(firebaseUser));
+ authSpy['signInWithPopup'].and.returnValue(Promise.resolve(googleCredential));
+ authSpy['signInWithRedirect'].and.returnValue(Promise.resolve(AngularFireAuthState));
+ authSpy['signInWithCredential'].and.returnValue(Promise.resolve(firebaseUser));
+ authSpy['signInAnonymously'].and.returnValue(Promise.resolve(firebaseUser));
+ authSpy['signInWithCustomToken'].and.returnValue(Promise.resolve(firebaseUser));
+ authSpy['signInWithEmailAndPassword'].and.returnValue(Promise.resolve(firebaseUser));
+ authSpy['onAuthStateChanged']
+ .and.callFake((obs: Observer) => {
+ fbAuthObserver = obs;
+ });
+ authSpy['getRedirectResult'].and.returnValue(Promise.resolve(null));
+
+ inject([FirebaseApp, AngularFireAuth], (_app: firebase.app.App, _afAuth: AngularFireAuth) => {
+ app = _app;
+ afAuth = _afAuth;
+ authData = null;
+ authCb = null;
+ backend = new FirebaseSdkAuthBackend(app);
+ })();
+ });
+ afterEach(done => {
+ app.delete().then(done, done.fail);
});
it('should be an observable', () => {
- expect(injector.get(FirebaseAuth)).toBeAnInstanceOf(Observable);
- })
+ expect(afAuth).toBeAnInstanceOf(Observable);
+ });
- describe('AuthState', () => {
- beforeEach(() => {
- ref = injector.get(FirebaseRef);
- spyOn(ref, 'onAuth').and.callFake((fn: (a: any) => void) => {
- authCb = fn;
- if (authCb !== null) {
- authCb(authData);
- }
- });
- backend = new FirebaseSdkAuthBackend(ref);
- });
- function updateAuthState(_authData: any): void {
- authData = _authData;
- if (authCb !== null) {
- authCb(authData);
- }
- }
-
- it('should synchronously load firebase auth data', () => {
- updateAuthState(authState);
- let nextSpy = jasmine.createSpy('nextSpy');
- let auth = injector.get(FirebaseAuth);
-
- auth.subscribe(nextSpy);
- expect(nextSpy).toHaveBeenCalledWith(AngularFireAuthState);
- });
-
- it('should be null if user is not authed', () => {
- let nextSpy = jasmine.createSpy('nextSpy');
- let auth = injector.get(FirebaseAuth);
+ it('should emit auth updates', (done: any) => {
+ let count = 0;
+ fbAuthObserver.next(null);
+
+ // Check that the first value is null
+ afAuth
+ .take(1)
+ .do((authData) => {
+ expect(authData).toBe(null);
+ setTimeout(() => fbAuthObserver.next(firebaseUser));
+ })
+ .subscribe();
+
+ // Check the 2nd value emitted from the observable
+ afAuth
+ .skip(1)
+ .take(1)
+ .do((authData) => {
+ expect(authData.auth).toEqual(AngularFireAuthState.auth);
+ })
+ // Subsribes on next instead of complete to ensure a value is emitted
+ .subscribe(null, done.fail, done);
+ }, 10);
- auth.subscribe(nextSpy);
- expect(nextSpy).toHaveBeenCalledWith(null);
+ describe('AuthState', () => {
+ it('should asynchronously load firebase auth data', (done) => {
+ fbAuthObserver.next(firebaseUser);
+ afAuth
+ .take(1)
+ .subscribe((data) => {
+ expect(data.auth).toEqual(AngularFireAuthState.auth);
+ }, done.fail, done);
});
- it('should emit auth updates', (done: () => void) => {
- let nextSpy = jasmine.createSpy('nextSpy');
- let auth = injector.get(FirebaseAuth);
-
- auth.subscribe(nextSpy);
- expect(nextSpy).toHaveBeenCalledWith(null);
- setTimeout(() => {
- nextSpy.calls.reset();
-
- updateAuthState(authState);
- expect(nextSpy).toHaveBeenCalledWith(AngularFireAuthState);
- done();
- }, 1);
+ it('should be null if user is not authed', (done) => {
+ fbAuthObserver.next(null);
+ afAuth
+ .take(1)
+ .subscribe(authData => {
+ expect(authData).toBe(null);
+ }, done.fail, done);
});
});
- function getArgIndex(callbackName: string): number {
- //In the firebase API, the completion callback is the second argument for all but a few functions.
- switch (callbackName) {
- case 'authAnonymously':
- case 'onAuth':
- return 0;
- case 'authWithOAuthToken':
- return 2;
- default:
- return 1;
- }
- }
-
- // calls the firebase callback
- function callback(callbackName: string, callIndex?: number): Function {
- callIndex = callIndex || 0; //assume the first call.
- var argIndex = getArgIndex(callbackName);
- return (ref)[callbackName].calls.argsFor(callIndex)[argIndex];
- }
describe('firebaseAuthConfig', () => {
- beforeEach(() => {
- ref = jasmine.createSpyObj('ref',
- ['authWithCustomToken', 'authAnonymously', 'authWithPassword',
- 'authWithOAuthPopup', 'authWithOAuthRedirect', 'authWithOAuthToken',
- 'unauth', 'getAuth', 'onAuth', 'offAuth',
- 'createUser', 'changePassword', 'changeEmail', 'removeUser', 'resetPassword'
- ]);
- backend = new FirebaseSdkAuthBackend(ref);
- });
-
it('should return a provider', () => {
expect(firebaseAuthConfig({ method: AuthMethods.Password })).toBeAnInstanceOf(Provider);
});
@@ -161,31 +196,23 @@ describe('FirebaseAuth', () => {
let config = {
method: AuthMethods.Anonymous
};
- let auth = new FirebaseAuth(backend, config);
- auth.login();
- expect(ref.authAnonymously).toHaveBeenCalled();
- });
-
- it('should pass options on to login method', () => {
- let config = {
- method: AuthMethods.Anonymous,
- remember: 'default'
- };
- let auth = new FirebaseAuth(backend, config);
- auth.login();
- expect(ref.authAnonymously).toHaveBeenCalledWith(jasmine.any(Function), { remember: 'default' });
+ afAuth = new AngularFireAuth(backend, config);
+ afAuth.login();
+ expect(app.auth().signInAnonymously).toHaveBeenCalled();
});
it('should be overridden by login\'s arguments', () => {
let config = {
method: AuthMethods.Anonymous
};
- let auth = new FirebaseAuth(backend, config);
- auth.login({
+ afAuth = new AngularFireAuth(backend, config);
+ afAuth.login({
method: AuthMethods.Popup,
provider: AuthProviders.Google
});
- expect(ref.authWithOAuthPopup).toHaveBeenCalledWith('google', jasmine.any(Function), {});
+ var spyArgs = (app.auth().signInWithPopup).calls.argsFor(0)[0];
+ var googleProvider = new GoogleAuthProvider();
+ expect(app.auth().signInWithPopup).toHaveBeenCalledWith(googleProvider);
});
it('should be merged with login\'s arguments', () => {
@@ -194,143 +221,122 @@ describe('FirebaseAuth', () => {
provider: AuthProviders.Google,
scope: ['email']
};
- let auth = new FirebaseAuth(backend, config);
- auth.login({
+ afAuth = new AngularFireAuth(backend, config);
+ afAuth.login({
provider: AuthProviders.Github
});
- expect(ref.authWithOAuthPopup).toHaveBeenCalledWith('github', jasmine.any(Function), {
- scope: ['email']
- });
+ var githubProvider = new GithubAuthProvider();
+ githubProvider.addScope('email');
+ expect(app.auth().signInWithPopup).toHaveBeenCalledWith(githubProvider);
});
});
describe('createUser', () => {
- let auth: FirebaseAuth = null;
- let credentials = { email: 'myname', password: 'password' };
-
- beforeEach(() => {
- ref = jasmine.createSpyObj('ref',
- ['authWithCustomToken', 'authAnonymously', 'authWithPassword',
- 'authWithOAuthPopup', 'authWithOAuthRedirect', 'authWithOAuthToken',
- 'unauth', 'getAuth', 'onAuth', 'offAuth',
- 'createUser', 'changePassword', 'changeEmail', 'removeUser', 'resetPassword'
- ]);
- backend = new FirebaseSdkAuthBackend(ref);
- auth = new FirebaseAuth(backend);
- });
+ let credentials = { email: 'noreply@github.com', password: 'password' };
- it('should call createUser on a db reference', () => {
- auth.createUser(credentials);
- expect(ref.createUser)
- .toHaveBeenCalledWith(credentials, jasmine.any(Function));
+ it('should call createUser on the app reference', () => {
+ afAuth.createUser(credentials);
+ expect(app.auth().createUserWithEmailAndPassword)
+ .toHaveBeenCalledWith(credentials.email, credentials.password);
});
-
});
describe('login', () => {
- let auth: FirebaseAuth = null;
-
- beforeEach(() => {
- ref = jasmine.createSpyObj('ref',
- ['authWithCustomToken', 'authAnonymously', 'authWithPassword',
- 'authWithOAuthPopup', 'authWithOAuthRedirect', 'authWithOAuthToken',
- 'unauth', 'getAuth', 'onAuth', 'offAuth',
- 'createUser', 'changePassword', 'changeEmail', 'removeUser', 'resetPassword'
- ]);
- backend = new FirebaseSdkAuthBackend(ref);
- auth = new FirebaseAuth(backend);
- });
-
it('should reject if password is used without credentials', (done: any) => {
let config = {
method: AuthMethods.Password
};
- let auth = new FirebaseAuth(backend, config);
- auth.login().then(done.fail, done);
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login()
+ .then(done.fail, done);
});
it('should reject if custom token is used without credentials', (done: any) => {
let config = {
method: AuthMethods.CustomToken
};
- let auth = new FirebaseAuth(backend, config);
- auth.login().then(done.fail, done);;
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login()
+ .then(done.fail, done);
});
it('should reject if oauth token is used without credentials', (done: any) => {
let config = {
method: AuthMethods.OAuthToken
};
- let auth = new FirebaseAuth(backend, config);
- auth.login().then(done.fail, done);
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login()
+ .then(done.fail, done);
});
it('should reject if popup is used without a provider', (done: any) => {
let config = {
method: AuthMethods.Popup
};
- let auth = new FirebaseAuth(backend, config);
- auth.login().then(done.fail, done);
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login()
+ .then(done.fail, done);
});
it('should reject if redirect is used without a provider', (done: any) => {
let config = {
method: AuthMethods.Redirect
};
- let auth = new FirebaseAuth(backend, config);
- auth.login().then(done.fail, done);
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login()
+ .then(done.fail, done);
});
describe('authWithCustomToken', () => {
let options = {
- remember: 'default',
method: AuthMethods.CustomToken
};
- let credentials = {
- token: 'myToken'
- };
+ let credentials = 'myToken';
it('passes custom token to underlying method', () => {
- auth.login(credentials, options);
- expect(ref.authWithCustomToken)
- .toHaveBeenCalledWith('myToken', jasmine.any(Function), { remember: 'default' });
+ afAuth.login(credentials, options);
+ expect(app.auth().signInWithCustomToken)
+ .toHaveBeenCalledWith('myToken');
});
it('will reject the promise if authentication fails', (done: any) => {
- auth.login(credentials, options).then(done.fail, done);
- callback('authWithCustomToken')('myError');
+ authSpy['signInWithCustomToken'].and.returnValue(Promise.reject('error'));
+ afAuth.login(credentials, options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(credentials, options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authWithCustomToken')(null, authState);
+ afAuth.login(credentials, options)
+ .then(result => {
+ expect(result.auth).toEqual(AngularFireAuthState.auth);
+ })
+ .then(done, done.fail);
});
});
describe('authAnonymously', () => {
let options = {
- remember: 'default',
method: AuthMethods.Anonymous
};
+
it('passes options object to underlying method', () => {
- auth.login(options);
- expect(ref.authAnonymously).toHaveBeenCalledWith(jasmine.any(Function), { remember: 'default' });
+ afAuth.login(options);
+ expect(app.auth().signInAnonymously).toHaveBeenCalled();
});
it('will reject the promise if authentication fails', (done: any) => {
- auth.login(options).then(done.fail, done);
- callback('authAnonymously')('myError');
+ authSpy['signInAnonymously'].and.returnValue(Promise.reject('myError'));
+ afAuth.login(options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authAnonymously')(null, authState);
+ afAuth.login(options)
+ .then(result => {
+ expect(result.auth).toEqual(AngularFireAuthState.auth);
+ })
+ .then(done, done.fail);
+
});
});
@@ -347,33 +353,30 @@ describe('FirebaseAuth', () => {
email: 'david@fire.com',
password: 'supersecretpassword'
};
- let auth = new FirebaseAuth(backend, config);
- auth.login(credentials);
- expect(ref.authWithPassword).toHaveBeenCalledWith(credentials,
- jasmine.any(Function),
- { provider: config.provider });
+ let afAuth = new AngularFireAuth(backend, config);
+ afAuth.login(credentials);
+ expect(app.auth().signInWithEmailAndPassword).toHaveBeenCalledWith(credentials.email, credentials.password);
});
it('passes options and credentials object to underlying method', () => {
- auth.login(credentials, options);
- expect(ref.authWithPassword).toHaveBeenCalledWith(
- credentials,
- jasmine.any(Function),
- { remember: options.remember }
- );
+ afAuth.login(credentials, options);
+ expect(app.auth().signInWithEmailAndPassword).toHaveBeenCalledWith(
+ credentials.email,
+ credentials.password);
});
it('will revoke the promise if authentication fails', (done: any) => {
- auth.login(credentials, options).then(done.fail, done);
- callback('authWithPassword')('myError');
+ authSpy['signInWithEmailAndPassword'].and.returnValue(Promise.reject('myError'));
+ afAuth.login(credentials, options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(credentials, options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authWithPassword')(null, authState);
+ afAuth.login(credentials, options)
+ .then(result => {
+ expect(result.auth).toEqual(AngularFireAuthState.auth);
+ })
+ .then(done, done.fail);
});
});
@@ -382,29 +385,48 @@ describe('FirebaseAuth', () => {
method: AuthMethods.Popup,
provider: AuthProviders.Github
};
+
+ beforeEach(() => {
+ authSpy['signInWithPopup'].and.returnValue(Promise.resolve(githubCredential));
+ })
+
it('passes provider and options object to underlying method', () => {
let customOptions = Object.assign({}, options);
customOptions.scope = ['email'];
- auth.login(customOptions);
- expect(ref.authWithOAuthPopup).toHaveBeenCalledWith(
- 'github',
- jasmine.any(Function),
- { scope: ['email'] }
- );
+ afAuth.login(customOptions);
+ let githubProvider = new GithubAuthProvider();
+ githubProvider.addScope('email');
+ expect(app.auth().signInWithPopup).toHaveBeenCalledWith(githubProvider);
});
it('will reject the promise if authentication fails', (done: any) => {
- auth.login(options).then(done.fail, done);
- callback('authWithOAuthPopup')('myError');
+ authSpy['signInWithPopup'].and.returnValue(Promise.reject('myError'));
+ afAuth.login(options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authWithOAuthPopup')(null, authState);
+ afAuth.login(options)
+ .then(result => {
+ expect(result.auth).toEqual(AngularFireAuthState.auth);
+ })
+ .then(done, done.fail);
});
+
+ it('should include credentials in onAuth payload after logging in', (done) => {
+ afAuth
+ .take(1)
+ .do((user: FirebaseAuthState) => {
+ expect(user.github).toBe(githubCredential.credential);
+ })
+ .subscribe(done, done.fail);
+
+ afAuth.login(options)
+ .then(() => {
+ // Calling with undefined `github` value to mimick actual Firebase value
+ fbAuthObserver.next(firebaseUser);
+ });
+ }, 10);
});
describe('authWithOAuthRedirect', () => {
@@ -412,29 +434,48 @@ describe('FirebaseAuth', () => {
method: AuthMethods.Redirect,
provider: AuthProviders.Github
};
+
it('passes provider and options object to underlying method', () => {
let customOptions = Object.assign({}, options);
customOptions.scope = ['email'];
- auth.login(customOptions);
- expect(ref.authWithOAuthRedirect).toHaveBeenCalledWith(
- 'github',
- jasmine.any(Function),
- { scope: ['email'] }
- );
+ afAuth.login(customOptions);
+ let githubProvider = new GithubAuthProvider();
+ expect(app.auth().signInWithRedirect).toHaveBeenCalledWith(githubProvider);
});
it('will reject the promise if authentication fails', (done: any) => {
- auth.login(options).then(done.fail, done);
- callback('authWithOAuthRedirect')('myError');
+ authSpy['signInWithRedirect'].and.returnValue(Promise.reject('myError'));
+ afAuth.login(options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authWithOAuthRedirect')(null, authState);
+ afAuth.login(options)
+ .then(result => {
+ expect(result).toEqual(AngularFireAuthState);
+ })
+ .then(done, done.fail);
});
+
+ it('should include credentials in onAuth payload after logging in', (done) => {
+ authSpy['getRedirectResult'].and.returnValue(Promise.resolve(githubCredential));
+ afAuth
+ .do((user: FirebaseAuthState) => {
+ expect(user.github).toBe(githubCredential.credential);
+ })
+ .take(2)
+ .subscribe(null, done.fail, done);
+
+ afAuth.login(options)
+ .then(() => {
+ // Calling with undefined `github` value to mimick actual Firebase value
+ fbAuthObserver.next(firebaseUser);
+ })
+ .then(() => {
+ // Call it twice to make sure it caches the result
+ fbAuthObserver.next(firebaseUser);
+ });
+ }, 10);
});
describe('authWithOAuthToken', () => {
@@ -444,64 +485,41 @@ describe('FirebaseAuth', () => {
scope: ['email']
};
const token = 'GITHUB_TOKEN';
- const credentials = {
- token: token
- };
+ const credentials = GithubAuthProvider.credential(token);
+
it('passes provider, token, and options object to underlying method', () => {
- auth.login(credentials, options);
- expect(ref.authWithOAuthToken).toHaveBeenCalledWith(
- 'github',
- token,
- jasmine.any(Function),
- { scope: ['email'] }
- );
+ afAuth.login(credentials, options);
+ expect(app.auth().signInWithCredential).toHaveBeenCalledWith(credentials);
});
it('passes provider, OAuth credentials, and options object to underlying method', () => {
let customOptions = Object.assign({}, options);
customOptions.provider = AuthProviders.Twitter;
- let twitterCredentials = {
- "user_id": "",
- "oauth_token": "",
- "oauth_token_secret": ""
- };
- auth.login(twitterCredentials, customOptions);
- expect(ref.authWithOAuthToken).toHaveBeenCalledWith(
- 'twitter',
- twitterCredentials,
- jasmine.any(Function),
- { scope: ['email'] }
- );
+ let credentials = TwitterAuthProvider.credential('', '');
+ afAuth.login(credentials, customOptions);
+ expect(app.auth().signInWithCredential).toHaveBeenCalledWith(credentials);
});
it('will reject the promise if authentication fails', (done: any) => {
- let creds = {
- token: ''
- };
- auth.login(creds, options).then(done.fail, done);
- callback('authWithOAuthToken')('myError');
+ authSpy['signInWithCredential'].and.returnValue(Promise.reject('myError'));
+ afAuth.login(credentials, options)
+ .then(done.fail, done);
});
it('will resolve the promise upon authentication', (done: any) => {
- auth.login(credentials, options).then(result => {
- expect(result).toEqual(AngularFireAuthState);
- done();
- }, done.fail);
- callback('authWithOAuthToken')(null, authState);
+ afAuth.login(credentials, options)
+ .then(result => {
+ expect(result.auth).toEqual(AngularFireAuthState.auth);
+ })
+ .then(done, done.fail);
});
});
describe('unauth()', () => {
- it('will call unauth() on the backing ref if logged in', () => {
- (ref).getAuth.and.returnValue({ provider: 'twitter' }); auth.logout();
- expect(ref.unauth).toHaveBeenCalled();
- });
-
- it('will NOT call unauth() on the backing ref if NOT logged in', () => {
- (ref).getAuth.and.returnValue(null);
- auth.logout();
- expect(ref.unauth).not.toHaveBeenCalled();
+ it('will call unauth() on the backing ref', () => {
+ afAuth.logout();
+ expect(app.auth().signOut).toHaveBeenCalled();
});
});
});
diff --git a/src/providers/auth.ts b/src/providers/auth.ts
index 6c5ae4f95..1357ce9e2 100644
--- a/src/providers/auth.ts
+++ b/src/providers/auth.ts
@@ -1,20 +1,27 @@
import {Provider, Inject, provide, Injectable, Optional} from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Observer } from 'rxjs/Observer';
import {ReplaySubject} from 'rxjs/ReplaySubject';
-import {FirebaseRef, FirebaseAuthConfig} from '../tokens';
+
+import 'rxjs/add/operator/mergeMap';
+import 'rxjs/add/operator/take';
+import 'rxjs/add/operator/concat';
+import 'rxjs/add/operator/skip';
+import 'rxjs/add/observable/of';
+
+import {FirebaseApp, FirebaseAuthConfig} from '../tokens';
import {isPresent} from '../utils/utils';
import * as utils from '../utils/utils';
import {
+ authDataToAuthState,
AuthBackend,
AuthProviders,
AuthMethods,
- OAuthCredentials,
- OAuth1Credentials,
- OAuth2Credentials,
- AuthCredentials,
- FirebaseAuthState,
+ EmailPasswordCredentials,
+ OAuthCredential,
AuthConfiguration,
- FirebaseAuthDataAllProviders,
- authDataToAuthState
+ FirebaseAuthState,
+ stripProviderId
} from './auth_backend';
const kBufferSize = 1;
@@ -26,20 +33,39 @@ export const firebaseAuthConfig = (config: AuthConfiguration): Provider => {
};
@Injectable()
-export class FirebaseAuth extends ReplaySubject {
+export class AngularFireAuth extends ReplaySubject {
+ private _credentialCache: {[key:string]: OAuthCredential} = {};
constructor(private _authBackend: AuthBackend,
@Optional() @Inject(FirebaseAuthConfig) private _config?: AuthConfiguration) {
super(kBufferSize);
- this._authBackend.onAuth((authData) => this._emitAuthData(authData));
+ let firstPass = true;
+ this._authBackend.onAuth()
+ .mergeMap((authState: FirebaseAuthState) => {
+ // TODO: get rid of side effect
+ if (firstPass) {
+ firstPass = false;
+ return this._authBackend.getRedirectResult()
+ .map((userCredential: firebase.auth.UserCredential) => {
+ if (userCredential && userCredential.credential) {
+ authState = attachCredentialToAuthState(authState, userCredential.credential, userCredential.credential.provider);
+ this._credentialCache[userCredential.credential.provider] = userCredential.credential;
+ }
+ return authState;
+ })
+ }
+ return Observable.of(authState);
+ })
+ .subscribe((authData: FirebaseAuthState) => this._emitAuthData(authData));
}
- public login(config?: AuthConfiguration): Promise;
- public login(credentials?: FirebaseCredentials): Promise;
- public login(credentials: AuthCredentials, config?: AuthConfiguration): Promise;
- public login(obj1?: any, obj2?: AuthConfiguration): Promise {
+ public login(config?: AuthConfiguration): firebase.Promise;
+ // If logging in with email and password
+ public login(credentials?: EmailPasswordCredentials | firebase.auth.AuthCredential | string): firebase.Promise;
+ public login(credentials: EmailPasswordCredentials | firebase.auth.AuthCredential | string, config?: AuthConfiguration): firebase.Promise;
+ public login(obj1?: any, obj2?: AuthConfiguration): firebase.Promise {
let config: AuthConfiguration = null;
- let credentials: AuthCredentials = null;
+ let credentials: EmailPasswordCredentials | firebase.auth.AuthCredential | string = null;
if (arguments.length > 2) {
return this._reject('Login only accepts a maximum of two arguments.');
} else if (arguments.length == 2) {
@@ -74,33 +100,37 @@ export class FirebaseAuth extends ReplaySubject {
switch (config.method) {
case AuthMethods.Popup:
- return this._authBackend.authWithOAuthPopup(config.provider, this._scrubConfig(config));
+ return this._authBackend.authWithOAuthPopup(config.provider, this._scrubConfig(config))
+ .then((userCredential: firebase.auth.UserCredential) => {
+ // Incorrect type information
+ this._credentialCache[userCredential.credential.provider] = userCredential.credential;
+ return authDataToAuthState(userCredential.user, (userCredential).credential);
+ });
case AuthMethods.Redirect:
- return this._authBackend.authWithOAuthRedirect(config.provider, this._scrubConfig(config));
+ // Gets around typings issue since this method doesn't resolve with a user.
+ // The method really only does anything with an error, since it redirects.
+ return >(this._authBackend).authWithOAuthRedirect(config.provider, this._scrubConfig(config));
case AuthMethods.Anonymous:
return this._authBackend.authAnonymously(this._scrubConfig(config));
case AuthMethods.Password:
- return this._authBackend.authWithPassword(credentials, this._scrubConfig(config, false));
+ return this._authBackend.authWithPassword(credentials);
case AuthMethods.OAuthToken:
- return this._authBackend.authWithOAuthToken(config.provider, credentials,
+ return this._authBackend.authWithOAuthToken(credentials,
this._scrubConfig(config));
case AuthMethods.CustomToken:
- return this._authBackend.authWithCustomToken((credentials).token,
- this._scrubConfig(config, false));
+ return this._authBackend.authWithCustomToken(credentials);
}
}
public logout(): void {
- if (this._authBackend.getAuth() !== null) {
- this._authBackend.unauth();
- }
+ this._authBackend.unauth();
}
- public getAuth(): FirebaseAuthData {
- return this._authBackend.getAuth();
+ public getAuth(): FirebaseAuthState {
+ return this._authBackend.getAuth()
}
- public createUser(credentials: FirebaseCredentials): Promise {
+ public createUser(credentials: EmailPasswordCredentials): firebase.Promise {
return this._authBackend.createUser(credentials);
}
@@ -115,10 +145,10 @@ export class FirebaseAuth extends ReplaySubject {
return Object.assign({}, this._config, config);
}
- private _reject(msg: string): Promise {
- return new Promise((res, rej) => {
+ private _reject(msg: string): firebase.Promise {
+ return (>new Promise((res, rej) => {
return rej(msg);
- });
+ }));
}
private _scrubConfig(config: AuthConfiguration, scrubProvider = true): any {
@@ -131,11 +161,26 @@ export class FirebaseAuth extends ReplaySubject {
}
- private _emitAuthData(authData: FirebaseAuthDataAllProviders): void {
+ private _emitAuthData(authData: FirebaseAuthState): void {
if (authData == null) {
this.next(null);
} else {
- this.next(authDataToAuthState(authData));
+ if (authData.auth && authData.auth.providerData && authData.auth.providerData[0]) {
+ let providerId = authData.auth.providerData[0].providerId;
+ let providerCredential = this._credentialCache[providerId];
+ if (providerCredential) {
+ authData = attachCredentialToAuthState(authData, providerCredential, providerId);
+ }
+ }
+
+ this.next(authData);
}
}
}
+
+function attachCredentialToAuthState (authState: FirebaseAuthState, credential, providerId: string): FirebaseAuthState {
+ if (!authState) return authState;
+ // TODO make authState immutable
+ authState[stripProviderId(providerId)] = credential;
+ return authState;
+}
\ No newline at end of file
diff --git a/src/providers/auth_backend.spec.ts b/src/providers/auth_backend.spec.ts
new file mode 100644
index 000000000..045e638c0
--- /dev/null
+++ b/src/providers/auth_backend.spec.ts
@@ -0,0 +1,106 @@
+import {
+ expect,
+ ddescribe,
+ describe,
+ it,
+ iit,
+ beforeEach
+} from '@angular/core/testing';
+
+import {
+ authDataToAuthState,
+ AuthProviders,
+ FirebaseAuthState,
+ CommonOAuthCredential,
+ GoogleCredential,
+ TwitterCredential
+} from './auth_backend';
+
+const baseFBUser = {
+ uid: '12345',
+ providerId: '',
+ providerData: [{}]
+};
+
+const baseAuthState: FirebaseAuthState = {
+ uid: baseFBUser.uid,
+ provider: AuthProviders.Anonymous,
+ auth: baseFBUser
+};
+
+const baseGithubCredential: CommonOAuthCredential = {
+ accessToken: 'GH_ACCESS_TOKEN',
+ provider: 'github.com'
+};
+
+const baseFacebookCredential: CommonOAuthCredential = {
+ accessToken: 'FB_ACCESS_TOKEN',
+ provider: 'facebook.com'
+};
+
+const baseGoogleCredential: GoogleCredential = {
+ idToken: 'GOOGLE_ID_TOKEN',
+ provider: 'google.com'
+};
+
+const baseTwitterCredential: TwitterCredential = {
+ accessToken: 'TWITTER_ACCESS_TOKEN',
+ provider: 'twitter.com',
+ secret: 'TWITTER_SECRET'
+};
+
+describe('auth_backend', () => {
+ describe('authDataToAuthState', () => {
+ it('Github: should return a FirebaseAuthState object with full provider data', () => {
+ let githubUser = Object.assign({}, baseFBUser, {
+ providerData: [{providerId: 'github.com'}]
+ });
+ let expectedAuthState = Object.assign({}, baseAuthState, {
+ github: baseGithubCredential,
+ auth: githubUser
+ });
+
+ let actualAuthState = authDataToAuthState(githubUser, baseGithubCredential);
+ expect(actualAuthState.github.accessToken).toEqual(baseGithubCredential.accessToken);
+ });
+ });
+
+ it('Google: should return a FirebaseAuthState object with full provider data', () => {
+ let googleUser = Object.assign({}, baseFBUser, {
+ providerData: [{providerId: 'google.com'}]
+ });
+ let expectedAuthState = Object.assign({}, baseAuthState, {
+ google: baseGoogleCredential,
+ auth: googleUser
+ });
+
+ let actualAuthState = authDataToAuthState(googleUser, baseGoogleCredential);
+ expect(actualAuthState.google.idToken).toEqual(baseGoogleCredential.idToken);
+ });
+
+ it('Twitter: should return a FirebaseAuthState object with full provider data', () => {
+ let twitterUser = Object.assign({}, baseFBUser, {
+ providerData: [{providerId: 'twitter.com'}]
+ });
+ let expectedAuthState = Object.assign({}, baseAuthState, {
+ twitter: baseTwitterCredential,
+ auth: twitterUser
+ });
+
+ let actualAuthState = authDataToAuthState(twitterUser, baseTwitterCredential);
+ expect(actualAuthState.twitter.secret).toEqual(baseTwitterCredential.secret);
+ });
+
+ it('Facebook: should return a FirebaseAuthState object with full provider data', () => {
+ let facebookUser = Object.assign({}, baseFBUser, {
+ providerData: [{providerId: 'facebook.com'}]
+ });
+ let expectedAuthState = Object.assign({}, baseAuthState, {
+ facebook: baseFacebookCredential,
+ auth: facebookUser
+ });
+
+ let actualAuthState = authDataToAuthState(facebookUser, baseFacebookCredential);
+ expect(actualAuthState.facebook.accessToken).toEqual(baseFacebookCredential.accessToken);
+ });
+});
diff --git a/src/providers/auth_backend.ts b/src/providers/auth_backend.ts
index 9d99a9010..5532dced3 100644
--- a/src/providers/auth_backend.ts
+++ b/src/providers/auth_backend.ts
@@ -1,25 +1,18 @@
+import { Observable } from 'rxjs/Observable';
+
export abstract class AuthBackend {
- abstract authWithCustomToken(token: string, options?: any): Promise;
+ abstract authWithCustomToken(token: string): Promise;
abstract authAnonymously(options?: any): Promise