diff --git a/functions/server/index.ts b/functions/server/index.ts index 3eb0fdc..2d2e5ae 100644 --- a/functions/server/index.ts +++ b/functions/server/index.ts @@ -1,6 +1,5 @@ import * as admin from "firebase-admin"; import * as functions from "firebase-functions"; -import { IDepartment } from "../shared/contract/IDepartment"; import { IStudent } from "../shared/contract/IStudent"; import { ModelMapper } from "./ModelMapper"; @@ -10,42 +9,6 @@ firestore.settings({ timestampsInSnapshots: true, }); -exports.onEducationDelete = functions.firestore.document("/educations/{id}").onDelete((snapshot) => { - const educationId = snapshot.id; - - // Delete relations on departments - const departmentsPromise = firestore.collection("/departments") - .get() - .then((querySnapshot) => { - const innerPromises = []; - - const mappedDepartments = ModelMapper.mapDocsToObjects(querySnapshot.docs); - mappedDepartments.forEach((department) => { - const educations = department.capacityPerEducation; - if (educations === undefined || educations === null || educations.length === 0) { - return; - } - - const updatedEducations = educations.filter((edu) => edu.educationId !== educationId); - if (updatedEducations.length === 0) { - return; - } - - if (educations.some((edu) => edu.educationId === educationId)) { - - const updatedDepartment: Partial = { - capacityPerEducation: updatedEducations, - }; - innerPromises.push(firestore.doc(`/departments/${department.id}`).update(updatedDepartment)); - } - }); - - return Promise.all([innerPromises]); - }); - - return Promise.all([departmentsPromise]); -}); - exports.onSchoolDelete = functions.firestore.document("/schools/{id}").onDelete((snapshot) => { const schoolId = snapshot.id; diff --git a/web/src/models/ModelBase.ts b/web/src/models/ModelBase.ts index a28508d..700d3b0 100644 --- a/web/src/models/ModelBase.ts +++ b/web/src/models/ModelBase.ts @@ -2,7 +2,7 @@ import moment from "moment"; import { IFirestoreEntityBase } from "../entities/IFirestoreEntityBase"; import { Firebase } from "../services/FirebaseInitializer"; -type DatabaseAction = "new" | "update"; +type DatabaseAction = "new" | "update" | "updateWithoutTime"; export abstract class ModelBase { @@ -22,15 +22,21 @@ export abstract class ModelBase { ...this.getEntityInternal(), }; + delete entity.id; + switch (databaseAction) { case "new": - delete entity.id; entity.createdTimestamp = Firebase.firestore.FieldValue.serverTimestamp() as firebase.firestore.Timestamp; entity.updatedTimestamp = Firebase.firestore.FieldValue.serverTimestamp() as firebase.firestore.Timestamp; break; case "update": + // Make sure to not overwrite the createdTimestamp. + delete entity.createdTimestamp; entity.updatedTimestamp = Firebase.firestore.FieldValue.serverTimestamp() as firebase.firestore.Timestamp; break; + case "updateWithoutTime": + delete entity.createdTimestamp; + break; default: throw new Error("Unsupported database action"); } diff --git a/web/src/services/repositories/DepartmentsRepository.ts b/web/src/services/repositories/DepartmentsRepository.ts index b57fc94..bf6a608 100644 --- a/web/src/services/repositories/DepartmentsRepository.ts +++ b/web/src/services/repositories/DepartmentsRepository.ts @@ -1,6 +1,7 @@ import { IDepartment } from "../../entities/IDepartment"; import { nameof } from "../../helpers/nameof"; import { Department } from "../../models/Department"; +import { Education } from "../../models/Education"; import { FirebaseModelMapper } from "../FirebaseModelMapper"; import { FirestoreRefs } from "../FirestoreRefs"; @@ -16,6 +17,15 @@ export class DepartmentsRepository { }); } + public static async getAllDepartments(): Promise { + const querySnapshot = await FirestoreRefs.getDepartmentCollectionRef() + .get(); + + const departmentEntities = FirebaseModelMapper.mapDocsToObjects(querySnapshot.docs); + const departments = departmentEntities.map((entity) => Department.fromEntity(entity)); + return departments; + } + public static async getDepartmentsByName(): Promise { const querySnapshot = await FirestoreRefs.getDepartmentCollectionRef() .orderBy(nameof("name"), "asc") @@ -31,12 +41,12 @@ export class DepartmentsRepository { .add(department.getEntity("new")); } - public static async updateDepartment(department: Department): Promise { + public static async updateDepartment(department: Department, doNotUpdateTimestamp: boolean = false): Promise { if (department.id === undefined) { return Promise.reject(Error("Department should have an id")); } await FirestoreRefs.getDepartmentDocRef(department.id) - .update(department.getEntity("update")); + .update(department.getEntity(doNotUpdateTimestamp ? "updateWithoutTime" : "update")); } public static async deleteDepartment(department: Department): Promise { @@ -46,4 +56,19 @@ export class DepartmentsRepository { await FirestoreRefs.getDepartmentDocRef(department.id) .delete(); } + + public static async deleteEducationRelations(education: Education): Promise { + const departments = await DepartmentsRepository.getAllDepartments(); + departments.forEach(async (department) => { + const educations = department.capacityPerEducation; + if (educations === undefined || educations === null || educations.length === 0) { + return; + } + if (educations.some((edu) => edu.educationId === education.id)) { + const educationsToKeep = educations.filter((edu) => edu.educationId !== education.id); + department.capacityPerEducation = educationsToKeep; + await DepartmentsRepository.updateDepartment(department, true); + } + }); + } } diff --git a/web/src/services/repositories/EducationsRepository.ts b/web/src/services/repositories/EducationsRepository.ts index 8bcc2b9..047318a 100644 --- a/web/src/services/repositories/EducationsRepository.ts +++ b/web/src/services/repositories/EducationsRepository.ts @@ -3,6 +3,8 @@ import { nameof } from "../../helpers/nameof"; import { Education } from "../../models/Education"; import { FirebaseModelMapper } from "../FirebaseModelMapper"; import { FirestoreRefs } from "../FirestoreRefs"; +import { DepartmentsRepository } from "./DepartmentsRepository"; +import { StudentsRepository } from "./StudentsRepository"; export class EducationsRepository { @@ -43,7 +45,18 @@ export class EducationsRepository { if (education.id === undefined) { return Promise.reject(Error("Education should have an id")); } + await FirestoreRefs.getEducationDocRef(education.id) .delete(); + + // Delete relations on departments + await DepartmentsRepository.deleteEducationRelations(education); + + // Delete relations on students + const students = await StudentsRepository.getStudentsWithEducation(education); + students.forEach(async (student) => { + student.educationId = undefined; + await StudentsRepository.updateStudent(student, true); + }); } } diff --git a/web/src/services/repositories/SchoolsRepository.ts b/web/src/services/repositories/SchoolsRepository.ts index 03c7f0f..08b8f89 100644 --- a/web/src/services/repositories/SchoolsRepository.ts +++ b/web/src/services/repositories/SchoolsRepository.ts @@ -3,6 +3,7 @@ import { nameof } from "../../helpers/nameof"; import { School } from "../../models/School"; import { FirebaseModelMapper } from "../FirebaseModelMapper"; import { FirestoreRefs } from "../FirestoreRefs"; +import { StudentsRepository } from "./StudentsRepository"; export class SchoolsRepository { @@ -45,5 +46,12 @@ export class SchoolsRepository { } await FirestoreRefs.getSchoolDocRef(school.id) .delete(); + + // Delete relations on students + const students = await StudentsRepository.getStudentsWithSchool(school); + students.forEach(async (student) => { + student.schoolId = undefined; + await StudentsRepository.updateStudent(student, true); + }); } } diff --git a/web/src/services/repositories/StudentsRepository.ts b/web/src/services/repositories/StudentsRepository.ts index 3aae6c4..6ee300e 100644 --- a/web/src/services/repositories/StudentsRepository.ts +++ b/web/src/services/repositories/StudentsRepository.ts @@ -1,5 +1,7 @@ import { IStudent } from "../../entities/IStudent"; import { nameof } from "../../helpers/nameof"; +import { Education } from "../../models/Education"; +import { School } from "../../models/School"; import { Student } from "../../models/Student"; import { FirebaseModelMapper } from "../FirebaseModelMapper"; import { FirestoreRefs } from "../FirestoreRefs"; @@ -28,17 +30,37 @@ export class StudentsRepository { return students; } + public static async getStudentsWithEducation(education: Education): Promise { + const querySnapshot = await FirestoreRefs.getStudentCollectionRef() + .where(nameof("educationId"), "==", education.id) + .get(); + + const studentEntities = FirebaseModelMapper.mapDocsToObjects(querySnapshot.docs); + const students = studentEntities.map((entity) => Student.fromEntity(entity)); + return students; + } + + public static async getStudentsWithSchool(school: School): Promise { + const querySnapshot = await FirestoreRefs.getStudentCollectionRef() + .where(nameof("schoolId"), "==", school.id) + .get(); + + const studentEntities = FirebaseModelMapper.mapDocsToObjects(querySnapshot.docs); + const students = studentEntities.map((entity) => Student.fromEntity(entity)); + return students; + } + public static async addStudent(student: Student): Promise { await FirestoreRefs.getStudentCollectionRef() .add(student.getEntity("new")); } - public static async updateStudent(student: Student): Promise { + public static async updateStudent(student: Student, doNotUpdateTimestamp: boolean = false): Promise { if (student.id === undefined) { return Promise.reject(Error("Student should have an id")); } await FirestoreRefs.getStudentDocRef(student.id) - .update(student.getEntity("update")); + .update(student.getEntity(doNotUpdateTimestamp ? "updateWithoutTime" : "update")); } public static async deleteStudent(student: Student): Promise {