-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: AI-categorized docs show in strategy sorts (PT-188476611)
[#188476611](https://www.pivotaltracker.com/story/show/188476611)
- Loading branch information
Showing
8 changed files
with
190 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import {FirestoreEvent, onDocumentWritten} from "firebase-functions/v2/firestore"; | ||
import {Change, DocumentSnapshot} from "firebase-functions/lib/v2/providers/firestore"; | ||
import * as admin from "firebase-admin"; | ||
|
||
/* | ||
* onDocumentTagged | ||
* Listens for changes to comments and updates the strategies array in the commented-on document's metadata. | ||
*/ | ||
export const onDocumentTagged = onDocumentWritten( | ||
"{root}/{space}/documents/{documentId}/comments/{commentId}", | ||
async (event: FirestoreEvent<Change<DocumentSnapshot> | undefined>) => { | ||
if (!event.data) return; | ||
|
||
const {root, space, documentId} = event.params; | ||
const firestore = admin.firestore(); | ||
const docKey = await firestore.collection(`${root}/${space}/documents`).doc(documentId).get().then((doc) => { | ||
return doc.data()?.key; | ||
}); | ||
if (!docKey) return; | ||
|
||
const collectionPath = `${root}/${space}/documents`; | ||
const documentCollection = admin.firestore().collection(collectionPath); | ||
const documentSnapshots = await documentCollection.where("key", "==", docKey).get(); | ||
const strategies: string[] = []; | ||
|
||
// Get all tags from all comments on the document and build an array of unique strategy values from them. | ||
for (const _documentSnapshot of documentSnapshots.docs) { | ||
const commentsUrl = `${_documentSnapshot.ref.path}/comments`; | ||
const commentCollection = admin.firestore().collection(commentsUrl); | ||
const commentSnapshots = await commentCollection.get(); | ||
|
||
for (const _commentSnapshot of commentSnapshots.docs) { | ||
const commentTags = _commentSnapshot.data()?.tags ?? []; | ||
|
||
if (commentTags != null && !Array.isArray(commentTags)) { | ||
console.warn("Found invalid comment tags", _commentSnapshot.ref.path, commentTags); | ||
continue; | ||
} | ||
|
||
commentTags.forEach((tag: string) => { | ||
if (tag && !strategies.includes(tag)) { | ||
strategies.push(tag); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
const metadataQuery = documentCollection.where("key", "==", docKey); | ||
const querySnapshot = await metadataQuery.get(); | ||
|
||
for (const doc of querySnapshot.docs) { | ||
const docRef = doc.ref; | ||
await docRef.update({strategies: [...strategies]}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import * as admin from "firebase-admin"; | ||
import {clearFirestoreData} from "firebase-functions-test/lib/providers/firestore"; | ||
import {onDocumentTagged} from "../src/on-document-tagged"; | ||
import {initialize, projectConfig} from "./initialize"; | ||
|
||
type CollectionRef = admin.firestore.CollectionReference<admin.firestore.DocumentData, admin.firestore.DocumentData>; | ||
|
||
const {fft, cleanup} = initialize(); | ||
|
||
describe("onDocumentTagged", () => { | ||
let documentCollection: CollectionRef; | ||
|
||
beforeEach(async () => { | ||
// The three lines below seemed to help prevent an "open handle" warning after the tests ran. They don't | ||
// actually solve the problem of Jest not exiting cleanly, but they do suppress the warning and so may be | ||
// helpful in further attempts to fix the problem. | ||
// const documents = await admin.firestore().collection("demo/test/documents").listDocuments(); | ||
// const deletePromises = documents.map((doc) => doc.delete()); | ||
// await Promise.all(deletePromises); | ||
|
||
await clearFirestoreData(projectConfig); | ||
|
||
documentCollection = admin.firestore().collection("demo/test/documents"); | ||
await documentCollection.doc("1234").set({ | ||
key: "doc-key", | ||
strategies: [], | ||
}); | ||
}); | ||
|
||
test("should add new values to a document's strategies array when a new comment is made", async () => { | ||
const wrapped = fft.wrap(onDocumentTagged); | ||
const commentRef = documentCollection.doc("1234").collection("comments").doc("5678"); | ||
|
||
await commentRef.set({tags: ["tag1", "tag2"]}); | ||
const event = { | ||
params: { | ||
root: "demo", | ||
space: "test", | ||
documentId: "1234", | ||
commentId: "5678", | ||
}, | ||
}; | ||
await wrapped(event); | ||
|
||
const docSnapshot = await documentCollection.doc("1234").get(); | ||
const docData = docSnapshot.data(); | ||
|
||
expect(docData).toEqual({ | ||
key: "doc-key", | ||
strategies: ["tag1", "tag2"], | ||
}); | ||
}); | ||
|
||
test("should remove values from a document's strategies array when an existing comment is deleted", async () => { | ||
const wrapped = fft.wrap(onDocumentTagged); | ||
const commentRef1 = documentCollection.doc("1234").collection("comments").doc("5678"); | ||
const commentRef2 = documentCollection.doc("1234").collection("comments").doc("9012"); | ||
|
||
// Add a comment with one tag. | ||
await commentRef1.set({tags: ["tag5"]}); | ||
const event1 = { | ||
params: { | ||
root: "demo", | ||
space: "test", | ||
documentId: "1234", | ||
commentId: "5678", | ||
}, | ||
}; | ||
await wrapped(event1); | ||
|
||
let docSnapshot = await documentCollection.doc("1234").get(); | ||
let docData = docSnapshot.data(); | ||
expect(docData).toEqual({ | ||
key: "doc-key", | ||
strategies: ["tag5"], | ||
}); | ||
|
||
// Add a second comment with one redundant tag and one new tag. | ||
await commentRef2.set({tags: ["tag5", "tag6"]}); | ||
const event2 = { | ||
params: { | ||
root: "demo", | ||
space: "test", | ||
documentId: "1234", | ||
commentId: "9012", | ||
}, | ||
}; | ||
await wrapped(event2); | ||
|
||
docSnapshot = await documentCollection.doc("1234").get(); | ||
docData = docSnapshot.data(); | ||
expect(docData).toEqual({ | ||
key: "doc-key", | ||
strategies: ["tag5", "tag6"], | ||
}); | ||
|
||
// Delete the second comment. | ||
await commentRef2.delete(); | ||
const event3 = { | ||
params: { | ||
root: "demo", | ||
space: "test", | ||
documentId: "1234", | ||
commentId: "9012", | ||
}, | ||
}; | ||
await wrapped(event3); | ||
|
||
// Verify that only the redundant tag remains in the strategies array. | ||
docSnapshot = await documentCollection.doc("1234").get(); | ||
docData = docSnapshot.data(); | ||
|
||
expect(docData).toEqual({ | ||
key: "doc-key", | ||
strategies: ["tag5"], | ||
}); | ||
}); | ||
|
||
afterAll(async () => { | ||
await cleanup(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters