Skip to content

Commit 2c2fe02

Browse files
authored
feat(rtdb): types for collection, audit trail, snapshot, and state changes (#1643)
1 parent 31045a9 commit 2c2fe02

13 files changed

+73
-63
lines changed

src/database/interfaces.ts

+19-6
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

+1-1
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

+8-16
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.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { isNil } from '../utils';
66

77
import { switchMap, distinctUntilChanged, scan } from 'rxjs/operators';
88

9-
export function listChanges<T>(ref: DatabaseQuery, events: ChildEvent[]): Observable<SnapshotAction[]> {
9+
export function listChanges<T=any>(ref: DatabaseQuery, events: ChildEvent[]): Observable<SnapshotAction<T>[]> {
1010
return fromRef(ref, 'value', 'once').pipe(
1111
switchMap(snapshotAction => {
1212
const childEvent$ = [of(snapshotAction)];
@@ -17,7 +17,7 @@ export function listChanges<T>(ref: DatabaseQuery, events: ChildEvent[]): Observ
1717
);
1818
}
1919

20-
function positionFor(changes: SnapshotAction[], key) {
20+
function positionFor<T>(changes: SnapshotAction<T>[], key) {
2121
const len = changes.length;
2222
for(let i=0; i<len; i++) {
2323
if(changes[i].payload.key === key) {
@@ -27,7 +27,7 @@ function positionFor(changes: SnapshotAction[], key) {
2727
return -1;
2828
}
2929

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

src/database/list/create-reference.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,51 @@
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';
88
import { map } from 'rxjs/operators';
99

10-
export function createListReference<T>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireList<T> {
10+
export function createListReference<T=any>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireList<T> {
1111
return {
1212
query,
1313
update: createDataOperationMethod<Partial<T>>(query.ref, 'update'),
1414
set: createDataOperationMethod<T>(query.ref, 'set'),
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

+1-1
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

+2-2
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

+2-2
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

+3-11
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,11 @@ import { fromRef } from '../observable/fromRef';
33
import { validateEventsArray } from './utils';
44
import { Observable, merge } from 'rxjs';
55

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

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

src/database/object/create-reference.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { DatabaseQuery, AngularFireObject } from '../interfaces';
33
import { createObjectSnapshotChanges } from './snapshot-changes';
44
import { AngularFireDatabase } from '../database';
55

6-
export function createObjectReference<T>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireObject<T> {
6+
export function createObjectReference<T=any>(query: DatabaseQuery, afDatabase: AngularFireDatabase): AngularFireObject<T> {
77
return {
88
query,
99
snapshotChanges<T>() {
10-
const snapshotChanges$ = createObjectSnapshotChanges(query)();
10+
const snapshotChanges$ = createObjectSnapshotChanges<T>(query)();
1111
return afDatabase.scheduler.keepUnstableUntilFirst(
1212
afDatabase.scheduler.runOutsideAngular(
1313
snapshotChanges$
+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { Observable } from 'rxjs';
22
import { fromRef } from '../observable/fromRef';
3-
import { DatabaseQuery, AngularFireAction, SnapshotAction } from '../interfaces';
4-
import { DataSnapshot } from '@firebase/database-types';
3+
import { DatabaseQuery, DatabaseSnapshot, AngularFireAction, SnapshotAction } from '../interfaces';
54

6-
export function createObjectSnapshotChanges(query: DatabaseQuery) {
7-
return function snapshotChanges(): Observable<SnapshotAction> {
5+
export function createObjectSnapshotChanges<T>(query: DatabaseQuery) {
6+
return function snapshotChanges(): Observable<SnapshotAction<T>> {
87
return fromRef(query, 'value');
98
}
109
}

src/database/observable/fromRef.spec.ts

+1-1
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, fromRef } from 'angularfire2/database';
44
import { TestBed, inject } from '@angular/core/testing';
55
import { COMMON_CONFIG } from '../test-config';

src/database/observable/fromRef.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { Observable } from 'rxjs';
33
import { FirebaseZoneScheduler } from 'angularfire2';
44
import { map, delay, share } from 'rxjs/operators';
55

6-
interface SnapshotPrevKey {
7-
snapshot: DatabaseSnapshot;
6+
interface SnapshotPrevKey<T> {
7+
snapshot: DatabaseSnapshot<T>;
88
prevKey: string | null | undefined;
99
}
1010

@@ -13,8 +13,8 @@ interface SnapshotPrevKey {
1313
* @param ref Database Reference
1414
* @param event Listen event type ('value', 'added', 'changed', 'removed', 'moved')
1515
*/
16-
export function fromRef(ref: DatabaseQuery, event: ListenEvent, listenType = 'on'): Observable<AngularFireAction<DatabaseSnapshot>> {
17-
return new Observable<SnapshotPrevKey>(subscriber => {
16+
export function fromRef<T>(ref: DatabaseQuery, event: ListenEvent, listenType = 'on'): Observable<AngularFireAction<DatabaseSnapshot<T>>> {
17+
return new Observable<SnapshotPrevKey<T>>(subscriber => {
1818
const fn = ref[listenType](event, (snapshot, prevKey) => {
1919
subscriber.next({ snapshot, prevKey });
2020
if (listenType == 'once') { subscriber.complete(); }
@@ -25,7 +25,7 @@ export function fromRef(ref: DatabaseQuery, event: ListenEvent, listenType = 'on
2525
return { unsubscribe() { } };
2626
}
2727
}).pipe(
28-
map((payload: SnapshotPrevKey) => {
28+
map(payload => {
2929
const { snapshot, prevKey } = payload;
3030
let key: string | null = null;
3131
if (snapshot.exists()) { key = snapshot.key; }

0 commit comments

Comments
 (0)