Skip to content

Commit d038272

Browse files
committed
feat(rtdb): types for collection, audit trail, snapshot, and state changes
1 parent 7d2fd53 commit d038272

File tree

14 files changed

+77
-67
lines changed

14 files changed

+77
-67
lines changed

src/database/interfaces.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export type FirebaseOperation = string | Reference | DataSnapshot;
66
export interface AngularFireList<T> {
77
query: DatabaseQuery;
88
valueChanges(events?: ChildEvent[]): Observable<T[]>;
9-
snapshotChanges(events?: ChildEvent[]): Observable<SnapshotAction[]>;
10-
stateChanges(events?: ChildEvent[]): Observable<SnapshotAction>;
11-
auditTrail(events?: ChildEvent[]): Observable<SnapshotAction[]>;
9+
snapshotChanges(events?: ChildEvent[]): Observable<SnapshotAction<T>[]>;
10+
stateChanges(events?: ChildEvent[]): Observable<SnapshotAction<T>>;
11+
auditTrail(events?: ChildEvent[]): Observable<SnapshotAction<T>[]>;
1212
update(item: FirebaseOperation, data: T): Promise<void>;
1313
set(item: FirebaseOperation, data: T): Promise<void>;
1414
push(data: T): ThenableReference;
@@ -18,7 +18,7 @@ export interface AngularFireList<T> {
1818
export interface AngularFireObject<T> {
1919
query: DatabaseQuery;
2020
valueChanges(): Observable<T | null>;
21-
snapshotChanges(): Observable<SnapshotAction>;
21+
snapshotChanges(): Observable<SnapshotAction<T>>;
2222
update(data: Partial<T>): Promise<void>;
2323
set(data: T): Promise<void>;
2424
remove(): Promise<void>;
@@ -45,11 +45,24 @@ export interface AngularFireAction<T> extends Action<T> {
4545
key: string | null;
4646
}
4747

48-
export type SnapshotAction = AngularFireAction<DatabaseSnapshot>;
48+
export type SnapshotAction<T> = AngularFireAction<DatabaseSnapshot<T>>;
4949

5050
export type Primitive = number | string | boolean;
5151

52-
export type DatabaseSnapshot = DataSnapshot;
52+
export interface DatabaseSnapshotExists<T> extends DataSnapshot {
53+
exists(): true;
54+
val(): T;
55+
forEach(action: (a: DatabaseSnapshot<T>) => boolean): boolean;
56+
}
57+
58+
export interface DatabaseSnapshotDoesNotExist<T> extends DataSnapshot {
59+
exists(): false;
60+
val(): null;
61+
forEach(action: (a: DatabaseSnapshot<T>) => boolean): boolean;
62+
}
63+
64+
export type DatabaseSnapshot<T> = DatabaseSnapshotExists<T> | DatabaseSnapshotDoesNotExist<T>;
65+
5366
export type DatabaseReference = Reference;
5467
export type DatabaseQuery = Query;
5568
export type QueryReference = DatabaseReference | DatabaseQuery;

src/database/list/audit-trail.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Reference } from '@firebase/database-types';
2-
import { FirebaseApp, FirebaseAppConfig, AngularFireModule } from 'angularfire2';
2+
import { FirebaseApp, AngularFireModule } from 'angularfire2';
33
import { AngularFireDatabase, AngularFireDatabaseModule, auditTrail, ChildEvent } from 'angularfire2/database';
44
import { TestBed, inject } from '@angular/core/testing';
55
import { COMMON_CONFIG } from '../test-config';

src/database/list/audit-trail.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,24 @@ import { AngularFireDatabase } from '../database';
77

88
import { skipWhile, withLatestFrom, map, scan } from 'rxjs/operators';
99

10-
export function createAuditTrail(query: DatabaseQuery, afDatabase: AngularFireDatabase) {
11-
return (events?: ChildEvent[]) => afDatabase.scheduler.keepUnstableUntilFirst(
12-
afDatabase.scheduler.runOutsideAngular(
13-
auditTrail(query, events)
14-
)
15-
);
16-
}
17-
18-
export function auditTrail(query: DatabaseQuery, events?: ChildEvent[]): Observable<SnapshotAction[]> {
19-
const auditTrail$ = stateChanges(query, events)
10+
export function auditTrail<T>(query: DatabaseQuery, events?: ChildEvent[]): Observable<SnapshotAction<T>[]> {
11+
const auditTrail$ = stateChanges<T>(query, events)
2012
.pipe(
21-
scan<SnapshotAction>((current, action) => [...current, action], [])
13+
scan<SnapshotAction<T>>((current, action) => [...current, action], [])
2214
);
23-
return waitForLoaded(query, auditTrail$);
15+
return waitForLoaded<T>(query, auditTrail$);
2416
}
2517

2618
interface LoadedMetadata {
2719
data: AngularFireAction<DataSnapshot>;
2820
lastKeyToLoad: any;
2921
}
3022

31-
function loadedData(query: DatabaseQuery): Observable<LoadedMetadata> {
23+
function loadedData<T>(query: DatabaseQuery): Observable<LoadedMetadata> {
3224
// Create an observable of loaded values to retrieve the
3325
// known dataset. This will allow us to know what key to
3426
// emit the "whole" array at when listening for child events.
35-
return fromRef(query, 'value')
27+
return fromRef<T>(query, 'value')
3628
.pipe(
3729
map(data => {
3830
// Store the last key in the data set
@@ -47,8 +39,8 @@ function loadedData(query: DatabaseQuery): Observable<LoadedMetadata> {
4739
);
4840
}
4941

50-
function waitForLoaded(query: DatabaseQuery, action$: Observable<SnapshotAction[]>) {
51-
const loaded$ = loadedData(query);
42+
function waitForLoaded<T>(query: DatabaseQuery, action$: Observable<SnapshotAction<T>[]>) {
43+
const loaded$ = loadedData<T>(query);
5244
return loaded$
5345
.pipe(
5446
withLatestFrom(action$),

src/database/list/changes.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('listChanges', () => {
6666

6767
it('should stream in order events', (done) => {
6868
const aref = ref(rando());
69-
const obs = listChanges(aref.orderByChild('name'), ['child_added']);
69+
const obs = listChanges<any>(aref.orderByChild('name'), ['child_added']);
7070
const sub = obs.pipe(take(1)).subscribe(changes => {
7171
const names = changes.map(change => change.payload.val().name);
7272
expect(names[0]).toEqual('one');
@@ -78,7 +78,7 @@ describe('listChanges', () => {
7878

7979
it('should stream in order events w/child_added', (done) => {
8080
const aref = ref(rando());
81-
const obs = listChanges(aref.orderByChild('name'), ['child_added']);
81+
const obs = listChanges<any>(aref.orderByChild('name'), ['child_added']);
8282
const sub = obs.pipe(skip(1),take(1)).subscribe(changes => {
8383
const names = changes.map(change => change.payload.val().name);
8484
expect(names[0]).toEqual('anotha one');
@@ -92,7 +92,7 @@ describe('listChanges', () => {
9292

9393
it('should stream events filtering', (done) => {
9494
const aref = ref(rando());
95-
const obs = listChanges(aref.orderByChild('name').equalTo('zero'), ['child_added']);
95+
const obs = listChanges<any>(aref.orderByChild('name').equalTo('zero'), ['child_added']);
9696
obs.pipe(skip(1),take(1)).subscribe(changes => {
9797
const names = changes.map(change => change.payload.val().name);
9898
expect(names[0]).toEqual('zero');
@@ -117,7 +117,7 @@ describe('listChanges', () => {
117117

118118
it('should process a new child_changed event', (done) => {
119119
const aref = ref(rando());
120-
const obs = listChanges(aref, ['child_added','child_changed'])
120+
const obs = listChanges<any>(aref, ['child_added','child_changed'])
121121
const sub = obs.pipe(skip(1),take(1)).subscribe(changes => {
122122
const data = changes.map(change => change.payload.val());
123123
expect(data[1].name).toEqual('lol');

src/database/list/changes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { isNil } from '../utils';
88

99
import { switchMap, distinctUntilChanged, scan } from 'rxjs/operators';
1010

11-
export function listChanges<T>(ref: DatabaseQuery, events: ChildEvent[]): Observable<SnapshotAction[]> {
11+
export function listChanges<T>(ref: DatabaseQuery, events: ChildEvent[]): Observable<SnapshotAction<T>[]> {
1212
return fromRef(ref, 'value', 'once').pipe(
1313
switchMap(snapshotAction => {
1414
const childEvent$ = [of(snapshotAction)];
@@ -19,7 +19,7 @@ export function listChanges<T>(ref: DatabaseQuery, events: ChildEvent[]): Observ
1919
);
2020
}
2121

22-
function positionFor(changes: SnapshotAction[], key) {
22+
function positionFor<T>(changes: SnapshotAction<T>[], key) {
2323
const len = changes.length;
2424
for(let i=0; i<len; i++) {
2525
if(changes[i].payload.key === key) {
@@ -29,7 +29,7 @@ function positionFor(changes: SnapshotAction[], key) {
2929
return -1;
3030
}
3131

32-
function positionAfter(changes: SnapshotAction[], prevKey?: string) {
32+
function positionAfter<T>(changes: SnapshotAction<T>[], prevKey?: string) {
3333
if(isNil(prevKey)) {
3434
return 0;
3535
} else {

src/database/list/create-reference.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DatabaseQuery, AngularFireList, ChildEvent } from '../interfaces';
22
import { snapshotChanges } from './snapshot-changes';
3-
import { createStateChanges } from './state-changes';
4-
import { createAuditTrail } from './audit-trail';
3+
import { stateChanges } from './state-changes';
4+
import { auditTrail } from './audit-trail';
55
import { createDataOperationMethod } from './data-operation';
66
import { createRemoveMethod } from './remove';
77
import { AngularFireDatabase } from '../database';
@@ -15,23 +15,37 @@ export function createListReference<T>(query: DatabaseQuery, afDatabase: Angular
1515
push: (data: T) => query.ref.push(data),
1616
remove: createRemoveMethod(query.ref),
1717
snapshotChanges(events?: ChildEvent[]) {
18-
const snapshotChanges$ = snapshotChanges(query, events);
18+
const snapshotChanges$ = snapshotChanges<T>(query, events);
1919
return afDatabase.scheduler.keepUnstableUntilFirst(
2020
afDatabase.scheduler.runOutsideAngular(
2121
snapshotChanges$
2222
)
2323
);
2424
},
25-
stateChanges: createStateChanges(query, afDatabase),
26-
auditTrail: createAuditTrail(query, afDatabase),
27-
valueChanges<T>(events?: ChildEvent[]) {
28-
const snapshotChanges$ = snapshotChanges(query, events);
25+
stateChanges(events?: ChildEvent[]) {
26+
const stateChanges$ = stateChanges<T>(query, events);
27+
return afDatabase.scheduler.keepUnstableUntilFirst(
28+
afDatabase.scheduler.runOutsideAngular(
29+
stateChanges$
30+
)
31+
);
32+
},
33+
auditTrail(events?: ChildEvent[]) {
34+
const auditTrail$ = auditTrail<T>(query, events)
35+
return afDatabase.scheduler.keepUnstableUntilFirst(
36+
afDatabase.scheduler.runOutsideAngular(
37+
auditTrail$
38+
)
39+
);
40+
},
41+
valueChanges(events?: ChildEvent[]) {
42+
const snapshotChanges$ = snapshotChanges<T>(query, events);
2943
return afDatabase.scheduler.keepUnstableUntilFirst(
3044
afDatabase.scheduler.runOutsideAngular(
3145
snapshotChanges$
3246
)
3347
).pipe(
34-
map(actions => actions.map(a => a.payload.val()))
48+
map(actions => actions.map(a => a.payload.val() as T))
3549
);
3650
}
3751
}

src/database/list/data-operation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function createDataOperationMethod<T>(ref: DatabaseReference, operation:
77
return checkOperationCases(item, {
88
stringCase: () => ref.child(<string>item)[operation](value),
99
firebaseCase: () => (<DatabaseReference>item)[operation](value),
10-
snapshotCase: () => (<DatabaseSnapshot>item).ref[operation](value)
10+
snapshotCase: () => (<DatabaseSnapshot<T>>item).ref[operation](value)
1111
});
1212
}
1313
}

src/database/list/remove.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import { DataSnapshot, Reference } from '@firebase/database-types';
55

66
// TODO(davideast): Find out why TS thinks this returns firebase.Primise
77
// instead of Promise.
8-
export function createRemoveMethod(ref: DatabaseReference) {
8+
export function createRemoveMethod<T>(ref: DatabaseReference) {
99
return function remove(item?: FirebaseOperation): any {
1010
if(!item) { return ref.remove(); }
1111
return checkOperationCases(item, {
1212
stringCase: () => ref.child(<string>item).remove(),
1313
firebaseCase: () => (<DatabaseReference>item).remove(),
14-
snapshotCase: () => (<DatabaseSnapshot>item).ref.remove()
14+
snapshotCase: () => (<DatabaseSnapshot<T>>item).ref.remove()
1515
});
1616
}
1717
}

src/database/list/snapshot-changes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { listChanges } from './changes';
33
import { DatabaseQuery, ChildEvent, SnapshotAction } from '../interfaces';
44
import { validateEventsArray } from './utils';
55

6-
export function snapshotChanges(query: DatabaseQuery, events?: ChildEvent[]): Observable<SnapshotAction[]> {
6+
export function snapshotChanges<T>(query: DatabaseQuery, events?: ChildEvent[]): Observable<SnapshotAction<T>[]> {
77
events = validateEventsArray(events);
8-
return listChanges(query, events!);
8+
return listChanges<T>(query, events!);
99
}

src/database/list/state-changes.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,11 @@ import { validateEventsArray } from './utils';
44
import { Observable } from 'rxjs';
55
import { merge } from 'rxjs/observable/merge';
66

7-
import { DataSnapshot } from '@firebase/database-types';
7+
import { DatabaseSnapshot } from '../interfaces';
88
import { AngularFireDatabase } from '../database';
99

10-
export function createStateChanges(query: DatabaseQuery, afDatabase: AngularFireDatabase) {
11-
return (events?: ChildEvent[]) => afDatabase.scheduler.keepUnstableUntilFirst(
12-
afDatabase.scheduler.runOutsideAngular(
13-
stateChanges(query, events)
14-
)
15-
);
16-
}
17-
18-
export function stateChanges(query: DatabaseQuery, events?: ChildEvent[]) {
10+
export function stateChanges<T>(query: DatabaseQuery, events?: ChildEvent[]) {
1911
events = validateEventsArray(events)!;
20-
const childEvent$ = events.map(event => fromRef(query, event));
12+
const childEvent$ = events.map(event => fromRef<T>(query, event));
2113
return merge(...childEvent$);
2214
}

0 commit comments

Comments
 (0)