diff --git a/FT_functions/functions/src/index.ts b/FT_functions/functions/src/index.ts index 22ef8916..34496c1a 100644 --- a/FT_functions/functions/src/index.ts +++ b/FT_functions/functions/src/index.ts @@ -9,6 +9,7 @@ import { } from "./functionConfig"; import { getTriggerType } from "./utils"; +//import {propagateChangesOnTrigger} from './propagates' export const FT = { [functionName]: functions.firestore @@ -16,24 +17,28 @@ export const FT = { .onWrite(async (change, context) => { const triggerType = getTriggerType(change); let promises: Promise[] = []; - try { - const sparkPromises = sparksConfig - .filter((sparkConfig) => sparkConfig.triggers.includes(triggerType)) - .map((sparkConfig) => spark(sparkConfig)(change, context)); - console.log( - `#${sparkPromises.length} sparks will be evaluted on ${triggerType}` + const sparkPromises = sparksConfig + .filter((sparkConfig) => sparkConfig.triggers.includes(triggerType)) + .map((sparkConfig) => spark(sparkConfig)(change, context)); + console.log( + `#${sparkPromises.length} sparks will be evaluted on ${triggerType}` + ); + promises = sparkPromises; + if (triggerType !== "delete") { + const derivativePromise = derivative(derivativesConfig)( + change, + context ); - promises = sparkPromises; - } catch (err) {} + promises.push(derivativePromise); + } + // const propagateChanges = propagateChangesOnTrigger(change,triggerType); + // promises.push(propagateChanges); + try { - if (triggerType !== "delete") { - const derivativePromise = derivative(derivativesConfig)( - change, - context - ); - promises.push(derivativePromise); - } - } catch (err) {} - await Promise.all(promises); + const result = await Promise.allSettled(promises); + console.log(result); + } catch (err) { + console.log(`caught error: ${err}`); + } }), }; diff --git a/FT_functions/functions/src/propagates/SourceFns.ts b/FT_functions/functions/src/propagates/SourceFns.ts new file mode 100644 index 00000000..fc2fd228 --- /dev/null +++ b/FT_functions/functions/src/propagates/SourceFns.ts @@ -0,0 +1,70 @@ +import { DocumentSnapshot } from "firebase-functions/lib/providers/firestore"; +import { rowReducer } from "../utils"; +const TARGET_SUB_COLLECTION = "_FT_TARGETS"; + +//sample target sub document + +// {collection**}/ +/** + +{ + targetRef:'test/id' + targetFieldKey:'fieldKey' + trackedFields:[] +} + */ + +// source changes Trigger + +// check and propagate any tracked changes to +export const propagateChanges = (docSnapshot: DocumentSnapshot) => + docSnapshot.ref + .collection(TARGET_SUB_COLLECTION) + .get() + .then((queryResult) => { + queryResult.docs.map(async (doc) => { + const { targetRef, targetFieldKey, trackedFields } = doc.data() as { + targetRef: FirebaseFirestore.DocumentReference; + targetFieldKey: string; + trackedFields: string[]; + }; + const targetDoc = await targetRef.get(); + if (!targetDoc.exists) return false; + const targetFieldValue = targetDoc.get(targetFieldKey); + const indexOfCurrentTarget = targetFieldValue.findIndex( + (element) => element.docPath === docSnapshot.ref.path + ); + if (indexOfCurrentTarget > -1) { + targetFieldValue[indexOfCurrentTarget].snapshot = rowReducer( + trackedFields, + docSnapshot.data() + ); + await targetRef.update({ [targetFieldKey]: targetFieldValue }); + return true; + } else return false; + }); + }); + +// when deleting a document all snapshot copies of it in +export const removeCopiesOfDeleteDoc = ( + sourceDocRef: FirebaseFirestore.DocumentReference +) => + sourceDocRef + .collection(TARGET_SUB_COLLECTION) + .get() + .then((queryResult) => { + queryResult.docs.map(async (doc) => { + const { targetRef, targetFieldKey } = doc.data() as { + targetRef: FirebaseFirestore.DocumentReference; + targetFieldKey: string; + }; + const targetDoc = await targetRef.get(); + const currentTargetFieldValue = targetDoc.get(targetFieldKey); + const newTargetFieldValue = currentTargetFieldValue.filter( + ({ docPath }: { docPath: string; snapshot: any }) => + docPath !== sourceDocRef.path + ); + await targetRef.update({ [targetFieldKey]: newTargetFieldValue }); + await doc.ref.delete(); + }); + }); diff --git a/FT_functions/functions/src/propagates/TargetFns.ts b/FT_functions/functions/src/propagates/TargetFns.ts new file mode 100644 index 00000000..9c320077 --- /dev/null +++ b/FT_functions/functions/src/propagates/TargetFns.ts @@ -0,0 +1,48 @@ +import { db } from "../firebaseConfig"; +const TARGET_SUB_COLLECTION = "_FT_TARGETS"; + +//sample target sub document + +// {collection**}/ +/** + +{ + targetRef:'test/id' + targetFieldKey:'fieldKey' + trackedFields:[] +} + */ + +// Target changes Trigger + +// add propagation reference from source subcollection +export const addTargetRef = ( + targetRef: FirebaseFirestore.DocumentReference, + sourceDocPath: string, + targetFieldKey: string, + trackedFields +) => + db + .collection(`${sourceDocPath}/${TARGET_SUB_COLLECTION}`) + .add({ targetRef, targetFieldKey, trackedFields }); + +// remove propagation reference from source subcollection +export const removeTargetRef = ( + targetRef: FirebaseFirestore.DocumentReference, + sourceDocPath: string +) => + db + .collection(`${sourceDocPath}/${TARGET_SUB_COLLECTION}`) + .where("targetRef", "==", targetRef) + .get() + .then((queryResult) => queryResult.docs.map((doc) => doc.ref.delete())); + +// removes all references of deleted targets +export const removeAllReferencesOnTargetDelete = ( + targetDocRef: FirebaseFirestore.DocumentReference +) => + db + .collectionGroup(TARGET_SUB_COLLECTION) + .where("targetRef", "==", targetDocRef) + .get() + .then((queryResult) => queryResult.docs.map((doc) => doc.ref.delete())); diff --git a/FT_functions/functions/src/propagates/index.ts b/FT_functions/functions/src/propagates/index.ts new file mode 100644 index 00000000..267d6060 --- /dev/null +++ b/FT_functions/functions/src/propagates/index.ts @@ -0,0 +1,17 @@ +import * as functions from "firebase-functions"; +import { propagateChanges, removeCopiesOfDeleteDoc } from "./SourceFns"; + +export const propagateChangesOnTrigger = ( + change: functions.Change, + triggerType: "delete" | "create" | "update" +) => { + switch (triggerType) { + case "update": + return propagateChanges(change.after); + case "delete": + return removeCopiesOfDeleteDoc(change.before.ref); + case "create": + default: + return new Promise(() => false); + } +}; diff --git a/FT_functions/functions/src/utils/index.ts b/FT_functions/functions/src/utils/index.ts index ee0a69df..97f74f65 100644 --- a/FT_functions/functions/src/utils/index.ts +++ b/FT_functions/functions/src/utils/index.ts @@ -46,6 +46,13 @@ export const getTriggerType = (change) => ? "create" : "delete"; +export const rowReducer = (fieldsToSync, row) => + fieldsToSync.reduce((acc: any, curr: string) => { + if (row[curr] !== undefined && row[curr] !== null) + return { ...acc, [curr]: row[curr] }; + else return acc; + }, {}); + export default { getSecret, hasRequiredFields,