From 4c42c0cb06823d8f162a6c62b2df36d7f561d468 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Wed, 31 Jul 2024 11:36:51 +1200 Subject: [PATCH 1/9] define and document required interfaces --- server/src/data-layer/models/firebase.ts | 76 ++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/server/src/data-layer/models/firebase.ts b/server/src/data-layer/models/firebase.ts index 98b05a206..0683ea693 100644 --- a/server/src/data-layer/models/firebase.ts +++ b/server/src/data-layer/models/firebase.ts @@ -187,3 +187,79 @@ export interface Event { */ max_occupancy?: number } + +/** + * Base type to be able to log events that admins perform on booking related resources in the admin view + */ +interface BookingHistory { + /** + * The time which the booking operation was performed. MUST be in UTC format + */ + timestamp: Timestamp + + /** + * The start of the operated on date range + */ + start_date: Timestamp + + /** + * The end of the operated on date range + */ + end_date: Timestamp + + /** + * The type of event that the admin performed, used for parsing on the front-end + * + * Each of these are associated with the following: + * + * - `"added_user_to_booking"`: {@link BookingAddedEvent} + * - `"removed_user_from_booking"`: {@link BookingDeletedEvent} + * - `"changed_date_availability"`: {@link BookingAvailabilityChangeEvent} + */ + event_type: + | "added_user_to_booking" + | "removed_user_from_booking" + | "changed_date_availability" +} + +/** + * Event used to track a user being **manually** added to a booking (only possible via admin view) + * + * @extends BookingHistory {@link BookingHistory} + */ +export interface BookingAddedEvent extends BookingHistory { + /** + * The id corresponding to the user who had a **manually** added booking + */ + uid: string +} + +/** + * Event used to track the removal of a user from a date range (only possible via admin view) + * + * @extends BookingHistory {@link BookingHistory} + */ +export interface BookingDeletedEvent extends BookingHistory { + /** + * The id corresponding to the user who had a **manually** deleted booking + */ + uid: string +} + +/** + * Event used to track the history of the availability of dates changing + * + * @extends BookingHistory {@link BookingHistory} + */ +export interface BookingAvailabilityChangeEvent extends BookingHistory { + /** + * The **signed** difference between the newly available slots and the previously available slots. + * + * For example, if the original available slots was 32, and the availability was set to 0, + * the `change` in the slots needs to be **0 - 32 = -32** + * + * And vice versa, if the original available slots was 16, and the availability was set to 32, + * the `change` would be **32 - 16 = 16** + */ + change: number +} From 9ed16c734ba088e0e14c41521e7390ec0d8d6c6c Mon Sep 17 00:00:00 2001 From: bcho892 Date: Wed, 31 Jul 2024 21:37:31 +1200 Subject: [PATCH 2/9] create bookingHistory collection --- server/src/data-layer/adapters/FirestoreCollections.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/src/data-layer/adapters/FirestoreCollections.ts b/server/src/data-layer/adapters/FirestoreCollections.ts index 65c6a2bdb..bc2561848 100644 --- a/server/src/data-layer/adapters/FirestoreCollections.ts +++ b/server/src/data-layer/adapters/FirestoreCollections.ts @@ -2,6 +2,9 @@ import "dotenv/config" import { Booking, + BookingAddedEvent, + BookingAvailabilityChangeEvent, + BookingDeletedEvent, BookingSlot, UserAdditionalInfo } from "data-layer/models/firebase" @@ -29,7 +32,10 @@ const firestore = Object.assign( const db = { users: firestore.collection("users"), bookings: firestore.collection("bookings"), - bookingSlots: firestore.collection("booking_slots") + bookingSlots: firestore.collection("booking_slots"), + bookingHistory: firestore.collection< + BookingAddedEvent | BookingDeletedEvent | BookingAvailabilityChangeEvent + >("booking_history") } as const export default db From b23ac44d6577fc862497313784dac2764cae21a9 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 13:20:05 +1200 Subject: [PATCH 3/9] create service with basic methods --- .../data-layer/adapters/FirestoreCollections.ts | 8 ++------ server/src/data-layer/models/common.ts | 14 ++++++++++++++ server/src/data-layer/models/firebase.ts | 11 +++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/server/src/data-layer/adapters/FirestoreCollections.ts b/server/src/data-layer/adapters/FirestoreCollections.ts index bc2561848..646e7deca 100644 --- a/server/src/data-layer/adapters/FirestoreCollections.ts +++ b/server/src/data-layer/adapters/FirestoreCollections.ts @@ -2,9 +2,7 @@ import "dotenv/config" import { Booking, - BookingAddedEvent, - BookingAvailabilityChangeEvent, - BookingDeletedEvent, + BookingHistoryEvent, BookingSlot, UserAdditionalInfo } from "data-layer/models/firebase" @@ -33,9 +31,7 @@ const db = { users: firestore.collection("users"), bookings: firestore.collection("bookings"), bookingSlots: firestore.collection("booking_slots"), - bookingHistory: firestore.collection< - BookingAddedEvent | BookingDeletedEvent | BookingAvailabilityChangeEvent - >("booking_history") + bookingHistory: firestore.collection("booking_history") } as const export default db diff --git a/server/src/data-layer/models/common.ts b/server/src/data-layer/models/common.ts index 5ce05a94d..30faaae1e 100644 --- a/server/src/data-layer/models/common.ts +++ b/server/src/data-layer/models/common.ts @@ -4,3 +4,17 @@ export type DocumentDataWithUid = T & { */ id: string } + +/** + * Utility type to determine + */ +export type PaginatedFirebaseResponse = { + /** + * The current "page" of data returned from querying + */ + data: T[] + /** + * The cursor of the next page, is `undefined` if no such cursor exists + */ + nextCursor?: string +} diff --git a/server/src/data-layer/models/firebase.ts b/server/src/data-layer/models/firebase.ts index 0683ea693..f6145bd3a 100644 --- a/server/src/data-layer/models/firebase.ts +++ b/server/src/data-layer/models/firebase.ts @@ -232,6 +232,7 @@ export interface BookingAddedEvent extends BookingHistory { * The id corresponding to the user who had a **manually** added booking */ uid: string + event_type: "added_user_to_booking" } /** @@ -244,6 +245,7 @@ export interface BookingDeletedEvent extends BookingHistory { * The id corresponding to the user who had a **manually** deleted booking */ uid: string + event_type: "removed_user_from_booking" } /** @@ -262,4 +264,13 @@ export interface BookingAvailabilityChangeEvent extends BookingHistory { * the `change` would be **32 - 16 = 16** */ change: number + event_type: "changed_date_availability" } + +/** + * Helper type to specify the possible datastruces for the booking history + */ +export type BookingHistoryEvent = + | BookingAddedEvent + | BookingDeletedEvent + | BookingAvailabilityChangeEvent From 30a9225082c38c422a23ef7e6353f08109ea0458 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 13:21:11 +1200 Subject: [PATCH 4/9] add files :clown_face: --- .../services/BookingHistoryService.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 server/src/data-layer/services/BookingHistoryService.ts diff --git a/server/src/data-layer/services/BookingHistoryService.ts b/server/src/data-layer/services/BookingHistoryService.ts new file mode 100644 index 000000000..49bf0839c --- /dev/null +++ b/server/src/data-layer/services/BookingHistoryService.ts @@ -0,0 +1,78 @@ +import db from "data-layer/adapters/FirestoreCollections" +import { + DocumentDataWithUid, + PaginatedFirebaseResponse +} from "data-layer/models/common" +import { + BookingAddedEvent, + BookingAvailabilityChangeEvent, + BookingDeletedEvent, + BookingHistoryEvent +} from "data-layer/models/firebase" + +class BookingHistoryService { + /** + * Stores a manual deletion of a booking (by admin) into the booking history collection + * + * @param event the required parameters defined by {@link BookingDeletedEvent} + */ + public async addBookingDeletedEvent(event: BookingDeletedEvent) { + await db.bookingHistory.add(event) + } + + /** + * Stores a manual creation of a booking (by admin) into the booking history collection + * + * @param event the required parameters defined by {@link BookingAddedEvent} + */ + public async addBookingAddedEvent(event: BookingAddedEvent) { + await db.bookingHistory.add(event) + } + + /** + * Stores a modification to the booking availability into the booking history collection + * + * @param event the required parameters defined by {@link BookingAvailabilityChangeEvent} + */ + public async addAvailibilityChangeEvent( + event: BookingAvailabilityChangeEvent + ) { + await db.bookingHistory.add(event) + } + + /** + * Fetches the **latest** page of booking history events. + * + * @param limit how many history events to fetch, defaults to `100` + * @param startAfter the firebase document snapshot to paginate from + * @returns the page of booking history items and a cursor pointing to the + * last `id` to use for pagination + */ + public async getAllHistory( + limit: number = 100, + startAfter?: FirebaseFirestore.DocumentSnapshot< + BookingHistoryEvent, + FirebaseFirestore.DocumentData + > + ): Promise< + PaginatedFirebaseResponse> + > { + const res = await db.bookingHistory + .orderBy("timestamp") + .startAfter(startAfter || 0) + .limit(limit) + .get() + + const historyPage: DocumentDataWithUid[] = + res.docs.map((event) => { + return { ...event.data(), id: event.id } + }) + + return { + data: historyPage, + nextCursor: res.docs[res.docs.length - 1]?.id || undefined + } + } +} + +export default BookingHistoryService From e5dd7443a68397856a561f12552f170d24913936 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 19:34:10 +1200 Subject: [PATCH 5/9] update function signatures --- .../src/data-layer/services/BookingHistoryService.test.ts | 1 + server/src/data-layer/services/BookingHistoryService.ts | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 server/src/data-layer/services/BookingHistoryService.test.ts diff --git a/server/src/data-layer/services/BookingHistoryService.test.ts b/server/src/data-layer/services/BookingHistoryService.test.ts new file mode 100644 index 000000000..a4f43c7e2 --- /dev/null +++ b/server/src/data-layer/services/BookingHistoryService.test.ts @@ -0,0 +1 @@ +describe("BookingHistoryService integration tests", () => {}) diff --git a/server/src/data-layer/services/BookingHistoryService.ts b/server/src/data-layer/services/BookingHistoryService.ts index 49bf0839c..12a9a7e01 100644 --- a/server/src/data-layer/services/BookingHistoryService.ts +++ b/server/src/data-layer/services/BookingHistoryService.ts @@ -16,7 +16,9 @@ class BookingHistoryService { * * @param event the required parameters defined by {@link BookingDeletedEvent} */ - public async addBookingDeletedEvent(event: BookingDeletedEvent) { + public async addBookingDeletedEvent( + event: BookingDeletedEvent + ): Promise { await db.bookingHistory.add(event) } @@ -25,7 +27,7 @@ class BookingHistoryService { * * @param event the required parameters defined by {@link BookingAddedEvent} */ - public async addBookingAddedEvent(event: BookingAddedEvent) { + public async addBookingAddedEvent(event: BookingAddedEvent): Promise { await db.bookingHistory.add(event) } @@ -36,7 +38,7 @@ class BookingHistoryService { */ public async addAvailibilityChangeEvent( event: BookingAvailabilityChangeEvent - ) { + ): Promise { await db.bookingHistory.add(event) } From e97a1070bd4c2a9f33eda09afd66d8e59e23f56a Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 20:21:26 +1200 Subject: [PATCH 6/9] add basic tests --- .../services/BookingHistoryService.test.ts | 67 ++++++++++++++++++- .../services/BookingHistoryService.ts | 17 ++--- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/server/src/data-layer/services/BookingHistoryService.test.ts b/server/src/data-layer/services/BookingHistoryService.test.ts index a4f43c7e2..466f87bc7 100644 --- a/server/src/data-layer/services/BookingHistoryService.test.ts +++ b/server/src/data-layer/services/BookingHistoryService.test.ts @@ -1 +1,66 @@ -describe("BookingHistoryService integration tests", () => {}) +import { dateToFirestoreTimeStamp } from "data-layer/adapters/DateUtils" +import BookingHistoryService from "./BookingHistoryService" +import { Timestamp } from "firebase-admin/firestore" +import db from "data-layer/adapters/FirestoreCollections" +import { cleanFirestore } from "test-config/TestUtils" + +const bookingHistoryService = new BookingHistoryService() +describe("BookingHistoryService integration tests", () => { + afterEach(async () => { + await cleanFirestore() + }) + + it("Should be able to add an event for deleted bookings", async () => { + const startDate = dateToFirestoreTimeStamp(new Date(2002, 10, 8)) + const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) + const currentTime = dateToFirestoreTimeStamp(new Date()) + + const newEvent = await bookingHistoryService.addBookingDeletedEvent({ + uid: "user-removed-from-booking", + start_date: startDate as Timestamp, + end_date: endDate as Timestamp, + event_type: "removed_user_from_booking", + timestamp: currentTime + }) + + const result = db.bookingHistory.doc("user-removed-from-booking").get() + + expect(result).toEqual(newEvent) + }) + + it("Should be able to add an event for created bookings", async () => { + const startDate = dateToFirestoreTimeStamp(new Date(2002, 10, 8)) + const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) + const currentTime = dateToFirestoreTimeStamp(new Date()) + + const newEvent = await bookingHistoryService.addBookingAddedEvent({ + uid: "user-added-to-booking", + start_date: startDate as Timestamp, + end_date: endDate as Timestamp, + event_type: "added_user_to_booking", + timestamp: currentTime + }) + + const result = db.bookingHistory.doc("user-added-to-booking").get() + + expect(result).toEqual(newEvent) + }) + + it("Should be able to add an event for availability changes", async () => { + const startDate = dateToFirestoreTimeStamp(new Date(2002, 10, 8)) + const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) + const currentTime = dateToFirestoreTimeStamp(new Date()) + + const newEvent = await bookingHistoryService.addAvailibilityChangeEvent({ + start_date: startDate as Timestamp, + end_date: endDate as Timestamp, + event_type: "changed_date_availability", + timestamp: currentTime, + change: -69 + }) + + const result = db.bookingHistory.doc(newEvent.id).get() + + expect(result).toEqual(newEvent) + }) +}) diff --git a/server/src/data-layer/services/BookingHistoryService.ts b/server/src/data-layer/services/BookingHistoryService.ts index 12a9a7e01..1516ea545 100644 --- a/server/src/data-layer/services/BookingHistoryService.ts +++ b/server/src/data-layer/services/BookingHistoryService.ts @@ -15,31 +15,32 @@ class BookingHistoryService { * Stores a manual deletion of a booking (by admin) into the booking history collection * * @param event the required parameters defined by {@link BookingDeletedEvent} + * @returns the created document */ - public async addBookingDeletedEvent( - event: BookingDeletedEvent - ): Promise { - await db.bookingHistory.add(event) + public async addBookingDeletedEvent(event: BookingDeletedEvent) { + return await db.bookingHistory.add(event) } /** * Stores a manual creation of a booking (by admin) into the booking history collection * * @param event the required parameters defined by {@link BookingAddedEvent} + * @returns the created document */ - public async addBookingAddedEvent(event: BookingAddedEvent): Promise { - await db.bookingHistory.add(event) + public async addBookingAddedEvent(event: BookingAddedEvent) { + return await db.bookingHistory.add(event) } /** * Stores a modification to the booking availability into the booking history collection * * @param event the required parameters defined by {@link BookingAvailabilityChangeEvent} + * @returns the created document */ public async addAvailibilityChangeEvent( event: BookingAvailabilityChangeEvent - ): Promise { - await db.bookingHistory.add(event) + ) { + return await db.bookingHistory.add(event) } /** From dcd4a291306852e3519c2258f2eb16e61aedab53 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 20:40:41 +1200 Subject: [PATCH 7/9] add test for date range query --- .../services/BookingHistoryService.test.ts | 56 +++++++++++++++++++ .../services/BookingHistoryService.ts | 23 ++++++++ 2 files changed, 79 insertions(+) diff --git a/server/src/data-layer/services/BookingHistoryService.test.ts b/server/src/data-layer/services/BookingHistoryService.test.ts index 466f87bc7..bafc582f6 100644 --- a/server/src/data-layer/services/BookingHistoryService.test.ts +++ b/server/src/data-layer/services/BookingHistoryService.test.ts @@ -63,4 +63,60 @@ describe("BookingHistoryService integration tests", () => { expect(result).toEqual(newEvent) }) + + it("Should be able to fetch history in between a range of dates", async () => { + /** + * In these tests we don't care about these + */ + const startDate = dateToFirestoreTimeStamp(new Date(2002, 10, 8)) + const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) + + const searchStartDate = dateToFirestoreTimeStamp(new Date(2001, 10, 6)) + const searchEndDate = dateToFirestoreTimeStamp(new Date(2001, 10, 9)) + + const availabilityEvent = + await bookingHistoryService.addAvailibilityChangeEvent({ + timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), + change: 69, + start_date: startDate, + end_date: endDate, + event_type: "changed_date_availability" + }) + + const deletedEvent = await bookingHistoryService.addBookingDeletedEvent({ + timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), + start_date: startDate, + end_date: endDate, + event_type: "removed_user_from_booking", + uid: "deleted-user" + }) + + const addedEvent = await bookingHistoryService.addBookingAddedEvent({ + timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), + start_date: startDate, + end_date: endDate, + event_type: "added_user_to_booking", + uid: "added-user" + }) + + const notIncludedEvent = await bookingHistoryService.addBookingAddedEvent({ + timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 10)), + start_date: startDate, + end_date: endDate, + event_type: "added_user_to_booking", + uid: "unincluded-user" + }) + + const foundEvents = + await bookingHistoryService.getAllHistoryBetweenDateRange( + searchStartDate, + searchEndDate + ) + + expect(foundEvents).toContainEqual(availabilityEvent) + expect(foundEvents).toContainEqual(addedEvent) + expect(foundEvents).toContainEqual(deletedEvent) + expect(foundEvents).not.toContainEqual(notIncludedEvent) + }) + it("Should be able to fetch the latest X events") }) diff --git a/server/src/data-layer/services/BookingHistoryService.ts b/server/src/data-layer/services/BookingHistoryService.ts index 1516ea545..084efdc93 100644 --- a/server/src/data-layer/services/BookingHistoryService.ts +++ b/server/src/data-layer/services/BookingHistoryService.ts @@ -1,3 +1,4 @@ +import { firestoreTimestampToDate } from "data-layer/adapters/DateUtils" import db from "data-layer/adapters/FirestoreCollections" import { DocumentDataWithUid, @@ -9,6 +10,7 @@ import { BookingDeletedEvent, BookingHistoryEvent } from "data-layer/models/firebase" +import { Timestamp } from "firebase-admin/firestore" class BookingHistoryService { /** @@ -43,6 +45,27 @@ class BookingHistoryService { return await db.bookingHistory.add(event) } + /** + * Returns all history events whose timestamps fall between the given timestamps + * + * @param startDate the first date to return history events for + * @param endDate the last date to return history events for + * @returns a list of all events that fall between the specified date range + */ + public async getAllHistoryBetweenDateRange( + startDate: Timestamp, + endDate: Timestamp + ): Promise[]> { + const res = await db.bookingHistory + .where("timestamp", ">=", firestoreTimestampToDate(startDate)) + .where("timestamp", "<=", firestoreTimestampToDate(endDate)) + .get() + + return res.docs.map((doc) => { + return { ...doc.data(), id: doc.id } + }) + } + /** * Fetches the **latest** page of booking history events. * From 3431de7976753b9007c88fb6a742e9851a7a68e3 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Fri, 2 Aug 2024 22:40:38 +1200 Subject: [PATCH 8/9] working tests --- .../services/BookingHistoryService.test.ts | 148 +++++++++++++----- .../services/BookingHistoryService.ts | 8 +- 2 files changed, 109 insertions(+), 47 deletions(-) diff --git a/server/src/data-layer/services/BookingHistoryService.test.ts b/server/src/data-layer/services/BookingHistoryService.test.ts index bafc582f6..2c564961a 100644 --- a/server/src/data-layer/services/BookingHistoryService.test.ts +++ b/server/src/data-layer/services/BookingHistoryService.test.ts @@ -15,17 +15,19 @@ describe("BookingHistoryService integration tests", () => { const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) const currentTime = dateToFirestoreTimeStamp(new Date()) - const newEvent = await bookingHistoryService.addBookingDeletedEvent({ + const event = { uid: "user-removed-from-booking", start_date: startDate as Timestamp, end_date: endDate as Timestamp, event_type: "removed_user_from_booking", timestamp: currentTime - }) + } as const + + const newEvent = await bookingHistoryService.addBookingDeletedEvent(event) - const result = db.bookingHistory.doc("user-removed-from-booking").get() + const result = (await db.bookingHistory.doc(newEvent.id).get()).data() - expect(result).toEqual(newEvent) + expect(result).toEqual(event) }) it("Should be able to add an event for created bookings", async () => { @@ -33,17 +35,19 @@ describe("BookingHistoryService integration tests", () => { const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) const currentTime = dateToFirestoreTimeStamp(new Date()) - const newEvent = await bookingHistoryService.addBookingAddedEvent({ + const event = { uid: "user-added-to-booking", start_date: startDate as Timestamp, end_date: endDate as Timestamp, event_type: "added_user_to_booking", timestamp: currentTime - }) + } as const + + const newEvent = await bookingHistoryService.addBookingAddedEvent(event) - const result = db.bookingHistory.doc("user-added-to-booking").get() + const result = (await db.bookingHistory.doc(newEvent.id).get()).data() - expect(result).toEqual(newEvent) + expect(result).toEqual(event) }) it("Should be able to add an event for availability changes", async () => { @@ -51,72 +55,130 @@ describe("BookingHistoryService integration tests", () => { const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) const currentTime = dateToFirestoreTimeStamp(new Date()) - const newEvent = await bookingHistoryService.addAvailibilityChangeEvent({ + const event = { start_date: startDate as Timestamp, end_date: endDate as Timestamp, event_type: "changed_date_availability", timestamp: currentTime, change: -69 - }) + } as const - const result = db.bookingHistory.doc(newEvent.id).get() + const newEvent = + await bookingHistoryService.addAvailibilityChangeEvent(event) - expect(result).toEqual(newEvent) - }) + const result = await (await db.bookingHistory.doc(newEvent.id).get()).data() - it("Should be able to fetch history in between a range of dates", async () => { + expect(result).toEqual(event) + }) + describe("Fetching events", () => { /** * In these tests we don't care about these */ const startDate = dateToFirestoreTimeStamp(new Date(2002, 10, 8)) const endDate = dateToFirestoreTimeStamp(new Date(2002, 10, 10)) - const searchStartDate = dateToFirestoreTimeStamp(new Date(2001, 10, 6)) - const searchEndDate = dateToFirestoreTimeStamp(new Date(2001, 10, 9)) - - const availabilityEvent = - await bookingHistoryService.addAvailibilityChangeEvent({ - timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), - change: 69, - start_date: startDate, - end_date: endDate, - event_type: "changed_date_availability" - }) + const availabilityEvent = { + timestamp: Timestamp.fromDate(new Date(2001, 10, 9)), + change: 69, + start_date: startDate, + end_date: endDate, + event_type: "changed_date_availability" + } as const - const deletedEvent = await bookingHistoryService.addBookingDeletedEvent({ - timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), + const deletedEvent = { + timestamp: Timestamp.fromDate(new Date(2001, 10, 9)), start_date: startDate, end_date: endDate, event_type: "removed_user_from_booking", uid: "deleted-user" - }) + } as const - const addedEvent = await bookingHistoryService.addBookingAddedEvent({ - timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 9)), + const addedEvent = { + timestamp: Timestamp.fromDate(new Date(2001, 10, 9)), start_date: startDate, end_date: endDate, event_type: "added_user_to_booking", uid: "added-user" - }) + } as const - const notIncludedEvent = await bookingHistoryService.addBookingAddedEvent({ - timestamp: dateToFirestoreTimeStamp(new Date(2001, 10, 10)), + const notIncludedEvent = { + timestamp: Timestamp.fromDate(new Date(2001, 10, 10)), start_date: startDate, end_date: endDate, event_type: "added_user_to_booking", uid: "unincluded-user" + } as const + + it("Should be able to fetch history in between a range of dates", async () => { + const searchStartDate = dateToFirestoreTimeStamp(new Date(2001, 10, 6)) + const searchEndDate = dateToFirestoreTimeStamp(new Date(2001, 10, 9)) + + const { id: availabilityId } = + await bookingHistoryService.addAvailibilityChangeEvent( + availabilityEvent + ) + + const { id: deletedId } = + await bookingHistoryService.addBookingDeletedEvent(deletedEvent) + + const { id: addedId } = + await bookingHistoryService.addBookingAddedEvent(addedEvent) + + const { id: notIncludedId } = + await bookingHistoryService.addBookingAddedEvent(notIncludedEvent) + + const foundEvents = + await bookingHistoryService.getAllHistoryBetweenDateRange( + searchStartDate, + searchEndDate + ) + + expect(foundEvents).toContainEqual({ ...addedEvent, id: addedId }) + expect(foundEvents).toContainEqual({ ...deletedEvent, id: deletedId }) + expect(foundEvents).toContainEqual({ + ...availabilityEvent, + id: availabilityId + }) + expect(foundEvents).not.toContainEqual({ + ...notIncludedEvent, + id: notIncludedId + }) }) - const foundEvents = - await bookingHistoryService.getAllHistoryBetweenDateRange( - searchStartDate, - searchEndDate - ) + it("Should be able to fetch the latest X events", async () => { + // Ordering matters! Earlier addition means it should be the first out + const { id: notIncludedId } = + await bookingHistoryService.addBookingAddedEvent(notIncludedEvent) + + const { id: availabilityId } = + await bookingHistoryService.addAvailibilityChangeEvent( + availabilityEvent + ) + + const { id: deletedId } = + await bookingHistoryService.addBookingDeletedEvent(deletedEvent) - expect(foundEvents).toContainEqual(availabilityEvent) - expect(foundEvents).toContainEqual(addedEvent) - expect(foundEvents).toContainEqual(deletedEvent) - expect(foundEvents).not.toContainEqual(notIncludedEvent) + const { id: addedId } = + await bookingHistoryService.addBookingAddedEvent(addedEvent) + + const PAGE_LENGTH = 3 as const + + const { data: foundEvents, nextCursor } = + await bookingHistoryService.getLatestHistory(PAGE_LENGTH) + + expect(nextCursor).not.toBeUndefined() + expect(foundEvents).toHaveLength(PAGE_LENGTH) + + expect(foundEvents).toContainEqual({ ...addedEvent, id: addedId }) + expect(foundEvents).toContainEqual({ ...deletedEvent, id: deletedId }) + expect(foundEvents).toContainEqual({ + ...availabilityEvent, + id: availabilityId + }) + expect(foundEvents).not.toContainEqual({ + ...notIncludedEvent, + id: notIncludedId + }) + }) }) - it("Should be able to fetch the latest X events") }) diff --git a/server/src/data-layer/services/BookingHistoryService.ts b/server/src/data-layer/services/BookingHistoryService.ts index 084efdc93..8eadb7ebe 100644 --- a/server/src/data-layer/services/BookingHistoryService.ts +++ b/server/src/data-layer/services/BookingHistoryService.ts @@ -17,7 +17,7 @@ class BookingHistoryService { * Stores a manual deletion of a booking (by admin) into the booking history collection * * @param event the required parameters defined by {@link BookingDeletedEvent} - * @returns the created document + * @returns the created document reference */ public async addBookingDeletedEvent(event: BookingDeletedEvent) { return await db.bookingHistory.add(event) @@ -27,7 +27,7 @@ class BookingHistoryService { * Stores a manual creation of a booking (by admin) into the booking history collection * * @param event the required parameters defined by {@link BookingAddedEvent} - * @returns the created document + * @returns the created document reference */ public async addBookingAddedEvent(event: BookingAddedEvent) { return await db.bookingHistory.add(event) @@ -37,7 +37,7 @@ class BookingHistoryService { * Stores a modification to the booking availability into the booking history collection * * @param event the required parameters defined by {@link BookingAvailabilityChangeEvent} - * @returns the created document + * @returns the created document reference */ public async addAvailibilityChangeEvent( event: BookingAvailabilityChangeEvent @@ -74,7 +74,7 @@ class BookingHistoryService { * @returns the page of booking history items and a cursor pointing to the * last `id` to use for pagination */ - public async getAllHistory( + public async getLatestHistory( limit: number = 100, startAfter?: FirebaseFirestore.DocumentSnapshot< BookingHistoryEvent, From 857a1c9108c4ac88ec10b358982278bc246883f3 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Sat, 3 Aug 2024 15:40:38 +1200 Subject: [PATCH 9/9] update documentation to be correct --- server/src/data-layer/models/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/data-layer/models/common.ts b/server/src/data-layer/models/common.ts index 30faaae1e..84afed258 100644 --- a/server/src/data-layer/models/common.ts +++ b/server/src/data-layer/models/common.ts @@ -6,7 +6,7 @@ export type DocumentDataWithUid = T & { } /** - * Utility type to determine + * Utility type for functions that return cursor-based pages */ export type PaginatedFirebaseResponse = { /**