Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(database): Add $ref to observables #447

Merged
merged 3 commits into from
Sep 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,72 @@ class App {
}

```

### FirebaseListObservable

Subclass of rxjs `Observable` which also has methods for updating
list-like Firebase data.

type: `class`
Type: `class`

Properties:

`$ref:(firebase.database.Reference)`: The reference used to sync this
collection to the Firebase database. See
[firebase.database.Reference](https://firebase.google.com/docs/reference/js/firebase.database.Reference)

Methods:

`push:(val) => Promise`: Add an element to the Firebase Database.
This is the equivalent of the Firebase SDK's
[set() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#set).
See [Saving Data](https://firebase.google.com/docs/database/web/save-data)
for info about restricted characters in object keys and more details about
saving data in the Database.

`update:(item:Object) => void`: Replace any child keys provided in `val`
with the values provided, but do not touch any other keys in the element.
This is the equivalent of the Firebase SDK's
[update() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#update).

`remove:([item]) => void`: Remove an element from the Firebase Database.
If no `item` argument is provided, it removes all elements from the list.
This is the equivalent of the Firebase SDK's
[remove() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove).

### FirebaseObjectObservable

Subclass of rxjs `Observable` which also has methods for syncing and
updating object-like Firebase data. {For collections and lists, see
FirebaseListObservable.)

Type: `class`

Properties:

`$ref:(firebase.database.Reference)`: The reference used to sync
this collection to the Firebase database. See
[firebase.database.Reference](https://firebase.google.com/docs/reference/js/firebase.database.Reference)

Methods:

`set:(val:any) => Promise`: Replaces any data at this path in the Database
with the value provided here (or adds the data if it doesn't exist).
This is the equivalent of the Firebase SDK's
[set() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#set).
See [Saving Data](https://firebase.google.com/docs/database/web/save-data)
for info about restricted characters in object keys and more details about
saving data in the Database.

additional methods:
`update:(val:Object) => void`: Replace any child keys provided in `val`
with the values provided here. The primary difference between this method
and `set()` above, is that `update()` modifies only the keys provided,
leaving any other data untouched, where `set()` essentially replaces
all data at the given path.
This is the equivalent of the Firebase SDK's
[update() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#update).

`add:(val) => void`: Add an element to the Firebase ref.
`remove:() => void`: Remove an element from the Firebase Database.
If no `item` argument is provided, it removes all elements from the list.
This is the equivalent of the Firebase SDK's
[remove() method](https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove).
6 changes: 3 additions & 3 deletions src/database/firebase_list_factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,8 @@ describe('FirebaseListFactory', () => {
firebase.database().ref().remove(done);
questions = FirebaseListFactory(`${rootDatabaseUrl}/questions`);
questionsSnapshotted = FirebaseListFactory(`${rootDatabaseUrl}/questionssnapshot`, { preserveSnapshot: true });
ref = (<any>questions)._ref;
refSnapshotted = (<any>questionsSnapshotted)._ref;
ref = (<any>questions).$ref;
refSnapshotted = (<any>questionsSnapshotted).$ref;
});

afterEach((done: any) => {
Expand All @@ -377,7 +377,7 @@ describe('FirebaseListFactory', () => {


it('should emit only when the initial data set has been loaded', (done: any) => {
(<any>questions)._ref.set([{ initial1: true }, { initial2: true }, { initial3: true }, { initial4: true }])
(<any>questions).$ref.set([{ initial1: true }, { initial2: true }, { initial3: true }, { initial4: true }])
.then(() => toPromise.call(skipAndTake(questions, 1)))
.then((val: any[]) => {
expect(val.length).toBe(4);
Expand Down
11 changes: 10 additions & 1 deletion src/database/firebase_list_observable.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FirebaseListObservable } from './index';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operator/map';
import { database } from 'firebase';
import { unwrapMapFn } from '../utils';
Expand Down Expand Up @@ -49,6 +48,16 @@ describe('FirebaseObservable', () => {
expect(map.call(O, noop) instanceof FirebaseListObservable).toBe(true);
});

describe('$ref', () => {
// it('should be a firebase.database.Reference', () => {
Copy link
Member

Choose a reason for hiding this comment

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

xit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't use xit because neither database.Reference nor firebase.database.Reference will compile in typescript until the bug is fixed : (

// expect(O.$ref instanceof database.Reference).toBe(true);
// });

it('should match the database path passed in the constructor', () => {
expect(O.$ref.toString()).toEqual(ref.toString());
});
});

describe('push', () => {
it('should throw an exception if pushed when not subscribed', () => {
O = new FirebaseListObservable(null, (observer:Observer<any>) => {});
Expand Down
28 changes: 14 additions & 14 deletions src/database/firebase_list_observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ import { Observable } from 'rxjs/Observable';
import { Operator } from 'rxjs/Operator';
import { Subscriber } from 'rxjs/Subscriber';
import { Subscription } from 'rxjs/Subscription';
import * as firebase from 'firebase';
import * as utils from '../utils';
import {
AFUnwrappedDataSnapshot,
FirebaseOperationCases
import {
AFUnwrappedDataSnapshot,
FirebaseOperationCases
} from '../interfaces';

export type FirebaseOperation = string | firebase.database.Reference | firebase.database.DataSnapshot | AFUnwrappedDataSnapshot;

export class FirebaseListObservable<T> extends Observable<T> {
constructor(public _ref: firebase.database.Reference | firebase.database.Query, subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void) {
constructor(public $ref: firebase.database.Reference | firebase.database.Query, subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void) {
super(subscribe);
}
lift<T, R>(operator: Operator<T, R>): Observable<R> {
const observable = new FirebaseListObservable<R>(this._ref);
const observable = new FirebaseListObservable<R>(this.$ref);
observable.source = this;
observable.operator = operator;
observable._ref = this._ref;
observable.$ref = this.$ref;
return observable;
}

push(val:any):firebase.database.ThenableReference {
if(!this._ref) {
if(!this.$ref) {
throw new Error('No ref specified for this Observable!');
}
this._ref.ref
return this._ref.ref.push(val);
return this.$ref.ref.push(val);
}

update(item: FirebaseOperation, value: Object): firebase.Promise<void> {
return this._checkOperationCases(item, {
stringCase: () => this._ref.ref.child(<string>item).update(value),
stringCase: () => this.$ref.ref.child(<string>item).update(value),
firebaseCase: () => (<firebase.database.Reference>item).update(value),
snapshotCase: () => (<firebase.database.DataSnapshot>item).ref.update(value),
unwrappedSnapshotCase: () => this._ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).update(value)
unwrappedSnapshotCase: () => this.$ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).update(value)
});
}

Expand All @@ -45,13 +45,13 @@ export class FirebaseListObservable<T> extends Observable<T> {

// 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(<string>item).remove(),
stringCase: () => this.$ref.ref.child(<string>item).remove(),
firebaseCase: () => (<firebase.database.Reference>item).remove(),
snapshotCase: () => (<firebase.database.DataSnapshot>item).ref.remove(),
unwrappedSnapshotCase: () => this._ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).remove()
unwrappedSnapshotCase: () => this.$ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).remove()
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/database/firebase_object_factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('FirebaseObjectFactory', () => {

it('should emit a null value if no value is present when subscribed', (done: any) => {
subscription = observable.subscribe(unwrapped => {
const expectedObject = { $key: (<any>observable)._ref.key, $value: null };
const expectedObject = { $key: (<any>observable).$ref.key, $value: null };
expect(unwrapped.$key).toEqual(expectedObject.$key);
expect(unwrapped.$value).toEqual(expectedObject.$value);
expect(unwrapped.$exists()).toEqual(false);
Expand Down
10 changes: 10 additions & 0 deletions src/database/firebase_object_observable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ describe('FirebaseObjectObservable', () => {
expect(map.call(O, noop) instanceof FirebaseObjectObservable).toBe(true);
});

describe('$ref', () => {
// it('should be a firebase.database.Reference', () => {
// expect(O.$ref instanceof database.Reference).toBe(true);
// });

it('should match the database path passed in the constructor', () => {
expect(O.$ref.toString()).toEqual(ref.toString());
});
});

describe('set', () => {

it('should call set on the underlying ref', (done:any) => {
Expand Down
17 changes: 9 additions & 8 deletions src/database/firebase_object_observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,35 @@ import { Observable } from 'rxjs/Observable';
import { Operator } from 'rxjs/Operator';
import { Subscriber } from 'rxjs/Subscriber';
import { Subscription } from 'rxjs/Subscription';
import * as firebase from 'firebase';

export class FirebaseObjectObservable<T> extends Observable<T> {
constructor(subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void, private _ref?:firebase.database.Reference) {
constructor(subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void, public $ref?:firebase.database.Reference) {
super(subscribe);
}
lift<T, R>(operator: Operator<T, R>): Observable<R> {
const observable = new FirebaseObjectObservable<R>();
observable.source = this;
observable.operator = operator;
observable._ref = this._ref;
observable.$ref = this.$ref;
return observable;
}
set(value: any): firebase.Promise<void> {
if(!this._ref) {
if(!this.$ref) {
throw new Error('No ref specified for this Observable!');
}
return this._ref.set(value);
return this.$ref.set(value);
}
update(value: Object): firebase.Promise<void> {
if(!this._ref) {
if(!this.$ref) {
throw new Error('No ref specified for this Observable!');
}
return this._ref.update(value);
return this.$ref.update(value);
}
remove(): firebase.Promise<void> {
if(!this._ref) {
if(!this.$ref) {
throw new Error('No ref specified for this Observable!');
}
return this._ref.remove();
return this.$ref.remove();
}
}