-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathaudit-trail.ts
More file actions
62 lines (57 loc) · 2.48 KB
/
audit-trail.ts
File metadata and controls
62 lines (57 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import { AngularFireAction, ChildEvent, DatabaseQuery, DataSnapshot, SnapshotAction } from '../interfaces';
import { stateChanges } from './state-changes';
import { Observable, SchedulerLike } from 'rxjs';
import { fromRef } from '../observable/fromRef';
import { map, scan, skipWhile, withLatestFrom } from 'rxjs/operators';
export function auditTrail<T>(query: DatabaseQuery, events?: ChildEvent[], scheduler?: SchedulerLike): Observable<SnapshotAction<T>[]> {
const auditTrail$ = stateChanges<T>(query, events)
.pipe(
scan((current, action) => [...current, action], [])
);
return waitForLoaded<T>(query, auditTrail$, scheduler);
}
interface LoadedMetadata {
data: AngularFireAction<DataSnapshot>;
lastKeyToLoad: any;
}
function loadedData<T>(query: DatabaseQuery, scheduler?: SchedulerLike): Observable<LoadedMetadata> {
// Create an observable of loaded values to retrieve the
// known dataset. This will allow us to know what key to
// emit the "whole" array at when listening for child events.
return fromRef<T>(query, 'value', 'on', scheduler)
.pipe(
map(data => {
// Store the last key in the data set
let lastKeyToLoad;
// Loop through loaded dataset to find the last key
data.payload.forEach(child => {
lastKeyToLoad = child.key; return false;
});
// return data set and the current last key loaded
return { data, lastKeyToLoad };
})
);
}
function waitForLoaded<T>(query: DatabaseQuery, action$: Observable<SnapshotAction<T>[]>, scheduler?: SchedulerLike) {
const loaded$ = loadedData<T>(query, scheduler);
return loaded$
.pipe(
withLatestFrom(action$),
// Get the latest values from the "loaded" and "child" datasets
// We can use both datasets to form an array of the latest values.
map(([loaded, actions]) => {
// Store the last key in the data set
const lastKeyToLoad = loaded.lastKeyToLoad;
// Store all child keys loaded at this point
const loadedKeys = actions.map(snap => snap.key);
return { actions, lastKeyToLoad, loadedKeys };
}),
// This is the magical part, only emit when the last load key
// in the dataset has been loaded by a child event. At this point
// we can assume the dataset is "whole".
skipWhile(meta => meta.loadedKeys.indexOf(meta.lastKeyToLoad) === -1),
// Pluck off the meta data because the user only cares
// to iterate through the snapshots
map(meta => meta.actions)
);
}