@magnetarjs/plugin-firestore-admin
Version:
Magnetar plugin firestore-admin
82 lines (81 loc) • 3.96 kB
JavaScript
import { getFirestoreCollectionPath, getFirestoreDocPath, } from '@magnetarjs/utils-firestore';
import { isString } from 'is-what';
import { docSnapshotToDocMetadata, getQueryInstance } from '../helpers/getFirestore.js';
export function streamActionFactory(firestorePluginOptions) {
return function ({ payload, collectionPath, docId, pluginModuleConfig, mustExecuteOnRead, }) {
const { added, modified, removed } = mustExecuteOnRead;
const { db } = firestorePluginOptions;
// Extract onFirstData callback from payload if provided
const onFirstData = payload?.onFirstData;
let resolveStream;
let rejectStream;
const streaming = new Promise((resolve, reject) => {
resolveStream = resolve;
rejectStream = reject;
});
let closeStream;
let firstDataReceived = false;
// in case of a doc module
if (isString(docId)) {
const documentPath = getFirestoreDocPath(collectionPath, docId, pluginModuleConfig, firestorePluginOptions); // prettier-ignore
closeStream = db
.doc(documentPath)
.onSnapshot((docSnapshot) => {
// even if `docSnapshot.metadata.hasPendingWrites`
// we should always execute `added/modified`
// because `core` handles overlapping calls for us
// Call onFirstData on first snapshot (whether doc exists or not)
if (!firstDataReceived && onFirstData) {
firstDataReceived = true;
setTimeout(() => onFirstData({ empty: !docSnapshot.exists }), 0);
}
// do nothing if the doc doesn't exist
if (!docSnapshot.exists)
return;
// serverChanges only
const docData = docSnapshot.data();
const docMetadata = docSnapshotToDocMetadata(docSnapshot);
if (docData)
added(docData, docMetadata);
}, rejectStream);
}
// in case of a collection module
else if (!docId) {
const _collectionPath = getFirestoreCollectionPath(collectionPath, pluginModuleConfig, firestorePluginOptions); // prettier-ignore
const queryInstance = getQueryInstance(_collectionPath, pluginModuleConfig, db);
closeStream = queryInstance.onSnapshot((querySnapshot) => {
// even if `docSnapshot.metadata.hasPendingWrites`
// we should always execute `added/modified`
// because `core` handles overlapping calls for us
// Call onFirstData on first snapshot (whether collection has docs or not)
if (!firstDataReceived && onFirstData) {
firstDataReceived = true;
setTimeout(() => onFirstData({ empty: querySnapshot.empty }), 0);
}
// serverChanges only
querySnapshot
.docChanges()
.forEach((docChange) => {
const docSnapshot = docChange.doc;
const docData = docSnapshot.data();
const docMetadata = docSnapshotToDocMetadata(docSnapshot);
if (docChange.type === 'added' && docData) {
added(docData, docMetadata);
}
if (docChange.type === 'modified' && docData) {
modified(docData, docMetadata);
}
if (docChange.type === 'removed') {
removed(docData, docMetadata);
}
});
}, rejectStream);
}
function stop() {
if (resolveStream)
resolveStream();
closeStream();
}
return { stop, streaming };
};
}