Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EventService CRUD methods #767

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions server/src/data-layer/adapters/Firestore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { admin } from "business-layer/security/Firebase"

const converter = <T>() => ({
toFirestore: (data: any) => data,
fromFirestore: (doc: any) => doc.data() as T
})

const firestore = Object.assign(
() => {
return admin.firestore()
},
{
doc: <T>(path: string) => {
return admin.firestore().doc(path).withConverter<T>(converter<T>())
},
collection: <T>(path: string) => {
return admin.firestore().collection(path).withConverter<T>(converter<T>())
},
subcollection: <T>(path: string, docId: string, subpath: string) => {
jeffplays2005 marked this conversation as resolved.
Show resolved Hide resolved
return admin
.firestore()
.collection(path) // The main collection
.doc(docId) // The document
.collection(subpath) // The sub collection
.withConverter<T>(converter<T>())
}
}
)

export default firestore
29 changes: 6 additions & 23 deletions server/src/data-layer/adapters/FirestoreCollections.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,20 @@
// credit https://plainenglish.io/blog/using-firestore-with-typescript-in-the-v9-sdk-cf36851bb099
import "dotenv/config"
import firestore from "./Firestore"
import {
Booking,
BookingHistoryEvent,
BookingSlot,
Event,
UserAdditionalInfo
} from "data-layer/models/firebase"
import { admin } from "business-layer/security/Firebase"

const converter = <T>() => ({
toFirestore: (data: any) => data,
fromFirestore: (doc: any) => doc.data() as T
})

const firestore = Object.assign(
() => {
return admin.firestore()
},
{
doc: <T>(path: string) => {
return admin.firestore().doc(path).withConverter<T>(converter<T>())
},
collection: <T>(path: string) => {
return admin.firestore().collection(path).withConverter<T>(converter<T>())
}
}
)

const db = {
const FirestoreCollections = {
users: firestore.collection<UserAdditionalInfo>("users"),
bookings: firestore.collection<Booking>("bookings"),
bookingSlots: firestore.collection<BookingSlot>("booking_slots"),
bookingHistory: firestore.collection<BookingHistoryEvent>("booking_history")
bookingHistory: firestore.collection<BookingHistoryEvent>("booking_history"),
events: firestore.collection<Event>("events")
} as const

export default db
export default FirestoreCollections
11 changes: 11 additions & 0 deletions server/src/data-layer/adapters/FirestoreSubcollections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// credit https://plainenglish.io/blog/using-firestore-with-typescript-in-the-v9-sdk-cf36851bb099
import "dotenv/config"
import firestore from "./Firestore"
import { EventReservation } from "data-layer/models/firebase"

const FirestoreSubcollections = {
reservations: (eventId: string) =>
firestore.subcollection<EventReservation>("events", eventId, "reservations")
} as const

export default FirestoreSubcollections
220 changes: 220 additions & 0 deletions server/src/data-layer/services/EventService.test.ts
jeffplays2005 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { cleanFirestore } from "test-config/TestUtils"
import EventService from "./EventService"
import {
dateToFirestoreTimeStamp,
removeUnderscoresFromTimestamp
} from "data-layer/adapters/DateUtils"
import { Event, EventReservation } from "data-layer/models/firebase"
import FirestoreCollections from "data-layer/adapters/FirestoreCollections"

const eventService = new EventService()

const startDate = dateToFirestoreTimeStamp(new Date(2024, 1, 1))
const endDate = dateToFirestoreTimeStamp(new Date(2024, 1, 2))

const event1: Event = {
title: "UASC new event",
description: "Grand opening of the website.",
location: "Virtual pizza event",
start_date: startDate,
end_date: endDate
}
const event2: Event = {
title: "Snowboard racing",
description: "Race and see who's the fastest!",
location: "Snowsport club",
start_date: startDate,
end_date: endDate
}

const reservation1: EventReservation = {
first_name: "John",
last_name: "Appleseed",
email: "[email protected]",
is_member: true
}
const reservation2: EventReservation = {
first_name: "Jane",
last_name: "Pearseed",
email: "[email protected]",
is_member: false
}

describe("EventService integration tests", () => {
afterEach(async () => {
await cleanFirestore()
})

it("Should be able to add an event", async () => {
const newEvent = await eventService.createEvent(event1)

const fetchedEvent = await FirestoreCollections.events
.doc(newEvent.id)
.get()

const data = fetchedEvent.data()

expect({
...data,
end_date: removeUnderscoresFromTimestamp(data.end_date),
start_date: removeUnderscoresFromTimestamp(data.start_date)
}).toEqual(event1)
})

it("Should be able to get an event", async () => {
const newEvent = await eventService.createEvent(event1)

const fetchedEvent = await eventService.getEventById(newEvent.id)

expect({
...fetchedEvent,
end_date: removeUnderscoresFromTimestamp(fetchedEvent.end_date),
start_date: removeUnderscoresFromTimestamp(fetchedEvent.start_date)
}).toEqual(event1)
})

it("Should be able to update an event", async () => {
const newEvent = await eventService.createEvent(event1)

await eventService.updateEvent(newEvent.id, {
title: "Wow pizza???"
})

const fetchedEvent = await eventService.getEventById(newEvent.id)

expect(fetchedEvent.title).toBe("Wow pizza???")
})

it("Should be able to delete an event", async () => {
const newEvent = await eventService.createEvent(event1)
await eventService.deleteEvent(newEvent.id)

const fetchedEvent = await eventService.getEventById(newEvent.id)

expect(fetchedEvent).toBe(undefined)
})

it("Should delete an event and also all reservations", async () => {
const newEvent = await eventService.createEvent(event1)
const newReservation1 = await eventService.addReservation(
newEvent.id,
reservation1
)
const newReservation2 = await eventService.addReservation(
newEvent.id,
reservation2
)

await eventService.deleteEvent(newEvent.id)

const fetchedReservation1 = await eventService.getReservation(
newEvent.id,
newReservation1.id
)
expect(fetchedReservation1).toBe(undefined)
const fetchedReservation2 = await eventService.getReservation(
newEvent.id,
newReservation2.id
)
expect(fetchedReservation2).toBe(undefined)
})

it("Should not delete other reservations when deleting an event document", async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const newEvent = await eventService.createEvent(event1)
await eventService.addReservation(newEvent.id, reservation1)
await eventService.addReservation(newEvent.id, reservation2)
const newEvent2 = await eventService.createEvent(event2)
const newReservation3 = await eventService.addReservation(
newEvent2.id,
reservation1
)
const newReservation4 = await eventService.addReservation(
newEvent2.id,
reservation2
)

await eventService.deleteEvent(newEvent.id)
const fetchedReservation3 = await eventService.getReservation(
newEvent2.id,
newReservation3.id
)
expect(fetchedReservation3).toEqual(reservation1)
const fetchedReservation4 = await eventService.getReservation(
newEvent2.id,
newReservation4.id
)
expect(fetchedReservation4).toEqual(reservation2)
})

/**
* Event reservation nested collection methods
*/
describe("EventReservation integration tests", () => {
it("Should be able to add a event reservation", async () => {
const newEvent = await eventService.createEvent(event1)

const reservation = await eventService.addReservation(
newEvent.id,
reservation1
)
const fetchedReservation = await FirestoreCollections.events
.doc(newEvent.id)
.collection("reservations") // subject to place as a constant somewhere
.doc(reservation.id)
.get()
expect(fetchedReservation.data()).toEqual(reservation1)
})

it("Should be able to get an event reservation", async () => {
const newEvent = await eventService.createEvent(event1)
const reservation = await eventService.addReservation(
newEvent.id,
reservation1
)
const fetchedReservation = await eventService.getReservation(
newEvent.id,
reservation.id
)
expect(fetchedReservation).toEqual(reservation1)
})

it("Should be able to update an event reservation", async () => {
const newEvent = await eventService.createEvent(event1)

const reservation = await eventService.addReservation(
newEvent.id,
reservation1
)

await eventService.updateReservation(newEvent.id, reservation.id, {
first_name: "Jan"
})

const fetchedReservation = await FirestoreCollections.events
.doc(newEvent.id)
.collection("reservations") // subject to place as a constant somewhere
.doc(reservation.id)
.get()

expect(fetchedReservation.data().first_name).toBe("Jan")
})

it("Should be able to delete an event reservation", async () => {
const newEvent = await eventService.createEvent(event1)

const reservation = await eventService.addReservation(
newEvent.id,
reservation1
)

await eventService.deleteReservation(newEvent.id, reservation.id)

const fetchedReservation = await FirestoreCollections.events
.doc(newEvent.id)
.collection("reservations") // subject to place as a constant somewhere
.doc(reservation.id)
.get()
expect(fetchedReservation.data()).toBe(undefined)
})
})
})
Loading
Loading