Skip to content

Commit b8a061b

Browse files
codediodeiodavideast
authored andcommitted
(rxfire): unwrap operator (#982)
* (rxfire): unwrap operator added a custom rxjs operator to map collections/docs to their data payload * (rxfire): rm unused lines from spec * (rxfire) data mapping functions refactored unwrap operator in favor of pure data mapping functions
1 parent d61fbb2 commit b8a061b

File tree

3 files changed

+125
-2
lines changed

3 files changed

+125
-2
lines changed

packages/rxfire/firestore/collection/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { firestore } from 'firebase/app';
1818
import { fromCollectionRef } from '../fromRef';
1919
import { Observable } from 'rxjs';
2020
import { map, filter, scan } from 'rxjs/operators';
21+
import { snapToData } from '../document';
2122

2223
const ALL_EVENTS: firestore.DocumentChangeType[] = [
2324
'added',
@@ -166,3 +167,18 @@ export function auditTrail(
166167
scan((current, action) => [...current, ...action], [])
167168
);
168169
}
170+
171+
/**
172+
* Returns a stream of documents mapped to their data payload, and optionally the document ID
173+
* @param query
174+
*/
175+
export function collectionData<T>(
176+
query: firestore.Query,
177+
idField?: string
178+
): Observable<T[]> {
179+
return collection(query).pipe(
180+
map(arr => {
181+
return arr.map(snap => snapToData(snap, idField) as T);
182+
})
183+
);
184+
}

packages/rxfire/firestore/document/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,30 @@
1616

1717
import { firestore } from 'firebase/app';
1818
import { fromDocRef } from '../fromRef';
19+
import { map } from 'rxjs/operators';
1920
import { Observable } from 'rxjs';
2021

2122
export function doc(ref: firestore.DocumentReference) {
2223
return fromDocRef(ref);
2324
}
25+
26+
/**
27+
* Returns a stream of a document, mapped to its data payload and optionally the docuument ID
28+
* @param query
29+
*/
30+
export function docData<T>(
31+
ref: firestore.DocumentReference,
32+
idField?: string
33+
): Observable<T> {
34+
return doc(ref).pipe(map(snap => snapToData(snap, idField) as T));
35+
}
36+
37+
export function snapToData(
38+
snapshot: firestore.QueryDocumentSnapshot,
39+
idField?: string
40+
) {
41+
return {
42+
...snapshot.data(),
43+
...(idField ? { [idField]: snapshot.id } : null)
44+
};
45+
}

packages/rxfire/test/firestore.test.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
1716
import { expect } from 'chai';
1817
import { initializeApp, firestore, app } from 'firebase/app';
1918
import 'firebase/firestore';
2019
import {
2120
collection,
2221
docChanges,
2322
sortedChanges,
24-
auditTrail
23+
auditTrail,
24+
docData,
25+
collectionData
2526
} from '../firestore';
2627
import { map, take, skip } from 'rxjs/operators';
2728

@@ -253,4 +254,88 @@ describe('RxFire Firestore', () => {
253254
davidDoc.update({ name: 'David!' });
254255
});
255256
});
257+
258+
describe('auditTrail', () => {
259+
/**
260+
* The `auditTrail()` method returns an array of every change that has
261+
* occured in the application. This test seeds two "people" into the
262+
* collection and checks that the two added events are there. It then
263+
* modifies a "person" and makes sure that event is on the array as well.
264+
*/
265+
it('should keep create a list of all changes', (done: MochaDone) => {
266+
const { colRef, expectedEvents, davidDoc } = seedTest(firestore);
267+
268+
const firstAudit = auditTrail(colRef).pipe(unwrapChange, take(1));
269+
const secondAudit = auditTrail(colRef).pipe(unwrapChange, skip(1));
270+
271+
firstAudit.subscribe(list => {
272+
expect(list).to.eql(expectedEvents);
273+
davidDoc.update({ name: 'David!' });
274+
});
275+
276+
secondAudit.subscribe(list => {
277+
const modifiedList = [
278+
...expectedEvents,
279+
{ name: 'David!', type: 'modified' }
280+
];
281+
expect(list).to.eql(modifiedList);
282+
done();
283+
});
284+
});
285+
286+
/**
287+
* This test seeds two "people" into the collection. The wrap operator then converts
288+
*/
289+
it('should filter the trail of events by event type', (done: MochaDone) => {
290+
const { colRef, davidDoc } = seedTest(firestore);
291+
292+
const modifiedAudit = auditTrail(colRef, ['modified']).pipe(unwrapChange);
293+
294+
modifiedAudit.subscribe(updateList => {
295+
const expectedEvents = [{ type: 'modified', name: 'David!' }];
296+
expect(updateList).to.eql(expectedEvents);
297+
done();
298+
});
299+
300+
davidDoc.update({ name: 'David!' });
301+
});
302+
});
303+
304+
describe('Data Mapping Functions', () => {
305+
/**
306+
* The `unwrap(id)` method will map a collection to its data payload and map the doc ID to a the specificed key.
307+
*/
308+
it('collectionData should map a QueryDocumentSnapshot[] to an array of plain objects', (done: MochaDone) => {
309+
const { colRef } = seedTest(firestore);
310+
311+
// const unwrapped = collection(colRef).pipe(unwrap('userId'));
312+
const unwrapped = collectionData(colRef, 'userId');
313+
314+
unwrapped.subscribe(val => {
315+
const expectedDoc = {
316+
name: 'David',
317+
userId: 'david'
318+
};
319+
expect(val).to.be.instanceof(Array);
320+
expect(val[0]).to.eql(expectedDoc);
321+
done();
322+
});
323+
});
324+
325+
it('docData should map a QueryDocumentSnapshot to a plain object', (done: MochaDone) => {
326+
const { davidDoc } = seedTest(firestore);
327+
328+
// const unwrapped = doc(davidDoc).pipe(unwrap('UID'));
329+
const unwrapped = docData(davidDoc, 'UID');
330+
331+
unwrapped.subscribe(val => {
332+
const expectedDoc = {
333+
name: 'David',
334+
UID: 'david'
335+
};
336+
expect(val).to.eql(expectedDoc);
337+
done();
338+
});
339+
});
340+
});
256341
});

0 commit comments

Comments
 (0)