From ac2e87dab37d1e660ca021873bfa5926aca190e2 Mon Sep 17 00:00:00 2001
From: ALGLAVE Ivan <ivan.alglave8@etu.univ-lorraine.fr>
Date: Thu, 9 Feb 2023 14:29:17 +0100
Subject: [PATCH] Added the ability to import multiple people at once using
 JSON format.

---
 src/groups/dao/groups.dao.ts           | 47 ++++++++++++++++++++++++++
 src/groups/groups.controller.ts        | 28 ++++++++++++++-
 src/groups/groups.module.ts            |  6 +++-
 src/groups/groups.service.ts           |  8 +++++
 src/internships/dao/internships.dao.ts |  2 --
 src/people/dao/people.dao.ts           | 37 ++++++++++++++++++++
 src/people/dto/create-people.dto.ts    | 10 +++---
 src/people/entities/people.entity.ts   |  2 +-
 src/people/people.controller.ts        |  2 +-
 src/people/people.service.ts           |  4 +++
 src/shared/Roles.ts                    | 15 ++++++++
 11 files changed, 149 insertions(+), 12 deletions(-)

diff --git a/src/groups/dao/groups.dao.ts b/src/groups/dao/groups.dao.ts
index 5be7864..52e31cc 100644
--- a/src/groups/dao/groups.dao.ts
+++ b/src/groups/dao/groups.dao.ts
@@ -107,6 +107,53 @@ export class GroupsDao {
       );
     });
 
+  findByIdAndUpdateManyByRole = (
+    id: string,
+    role: string,
+    peopleIds: string[],
+    action: string,
+  ): Promise<Group | void> =>
+    new Promise((resolve, reject) => {
+      let query = {};
+      if (action === 'post') {
+        switch (role) {
+          case Roles.ROLE_RESPONSIBLE:
+            query = { $addToSet: { responsibles: { $each: peopleIds } } };
+            break;
+          case Roles.ROLE_SECRETARY:
+            query = { $addToSet: { secretaries: { $each: peopleIds } } };
+            break;
+          case Roles.ROLE_STUDENT:
+            query = { $addToSet: { students: { $each: peopleIds } } };
+            break;
+          default:
+            reject(new BadRequestException('Bad role'));
+        }
+      } else if (action === 'delete') {
+        switch (role) {
+          case Roles.ROLE_RESPONSIBLE:
+            query = { $pull: { responsibles: { $in: peopleIds } } };
+            break;
+          case Roles.ROLE_SECRETARY:
+            query = { $pull: { secretaries: { $in: peopleIds } } };
+            break;
+          case Roles.ROLE_STUDENT:
+            query = { $pull: { students: { $in: peopleIds } } };
+            break;
+          default:
+            reject(new BadRequestException('Bad role'));
+        }
+      } else reject(new BadRequestException('Unknown action'));
+      this._groupModel.findByIdAndUpdate(
+        id,
+        query,
+        { new: true },
+        (err, value) => {
+          if (err) reject(err.message);
+          resolve(value);
+      });
+    });
+
   findByIdAndRemove = (id: string): Promise<Group | void> =>
     new Promise((resolve, reject) => {
       this._groupModel.findByIdAndDelete(id, {}, (err) => {
diff --git a/src/groups/groups.controller.ts b/src/groups/groups.controller.ts
index 18cf22e..625f5cc 100644
--- a/src/groups/groups.controller.ts
+++ b/src/groups/groups.controller.ts
@@ -13,11 +13,17 @@ import { CreateGroupDto } from './dto/create-group.dto';
 import { UpdateGroupDto } from './dto/update-group.dto';
 import { GroupEntity } from './entities/group.entity';
 import { GroupsService } from './groups.service';
+import { CreatePeopleDto } from 'src/people/dto/create-people.dto';
+import { PeopleService } from 'src/people/people.service';
+import { roleValue } from 'src/shared/Roles';
 
 @Controller('groups')
 @UseInterceptors(HttpInterceptor)
 export class GroupsController {
-  constructor(private readonly _groupsService: GroupsService) {}
+  constructor(
+    private readonly _groupsService: GroupsService,
+    private readonly _peopleService: PeopleService,
+  ) {}
 
   @Get()
   findAll(): Promise<GroupEntity[] | void> {
@@ -34,6 +40,26 @@ export class GroupsController {
     return this._groupsService.create(createGroupDto);
   }
 
+  @Post(':id/import/:role')
+  async importAsFile(
+    @Param() params: { id: string; role: string },
+    @Body() people: CreatePeopleDto[],
+  ): Promise<GroupEntity | void> {
+    const peopleEntities = await this._peopleService.createMany(
+      people.map((person) => {
+        person.role = roleValue(params.role);
+        return person;
+      }),
+    );
+    const peopleIds = peopleEntities.map((person) => person._id);
+    return this._groupsService.updateManyByRole(
+      params.id,
+      params.role,
+      peopleIds,
+      'post',
+    );
+  }
+
   @Put(':id')
   update(
     @Param() params: { id: string },
diff --git a/src/groups/groups.module.ts b/src/groups/groups.module.ts
index b8c3b67..3614a83 100644
--- a/src/groups/groups.module.ts
+++ b/src/groups/groups.module.ts
@@ -1,5 +1,8 @@
 import { Module, Logger } from '@nestjs/common';
 import { MongooseModule } from '@nestjs/mongoose';
+import { PeopleDao } from 'src/people/dao/people.dao';
+import { PeopleService } from 'src/people/people.service';
+import { People, PeopleSchema } from 'src/people/schemas/people.schema';
 import { GroupsDao } from './dao/groups.dao';
 import { GroupsController } from './groups.controller';
 import { GroupsService } from './groups.service';
@@ -8,8 +11,9 @@ import { Group, GroupSchema } from './schemas/group.schema';
 @Module({
   imports: [
     MongooseModule.forFeature([{ name: Group.name, schema: GroupSchema }]),
+    MongooseModule.forFeature([{ name: People.name, schema: PeopleSchema }]),
   ],
   controllers: [GroupsController],
-  providers: [GroupsService, GroupsDao, Logger],
+  providers: [GroupsService, GroupsDao, Logger, PeopleService, PeopleDao],
 })
 export class GroupsModule {}
diff --git a/src/groups/groups.service.ts b/src/groups/groups.service.ts
index bf52da3..59b939e 100644
--- a/src/groups/groups.service.ts
+++ b/src/groups/groups.service.ts
@@ -27,6 +27,14 @@ export class GroupsService {
   ): Promise<GroupEntity | void> =>
     this._groupsDao.findByIdAndUpdateRole(id, role, personId, action);
 
+  updateManyByRole = (
+    id: string,
+    role: string,
+    peopleIds: string[],
+    action: string,
+  ): Promise<GroupEntity | void> =>
+    this._groupsDao.findByIdAndUpdateManyByRole(id, role, peopleIds, action);
+
   delete(id: string): Promise<GroupEntity | void> {
     return this._groupsDao.findById(id).then((res) => {
       if (res) {
diff --git a/src/internships/dao/internships.dao.ts b/src/internships/dao/internships.dao.ts
index 22a0817..c42d59e 100644
--- a/src/internships/dao/internships.dao.ts
+++ b/src/internships/dao/internships.dao.ts
@@ -120,9 +120,7 @@ export class InternshipDao {
     content: string | boolean,
   ): Promise<Internship | void> =>
     new Promise((resolve, reject) => {
-      console.log('%s/%s: {%s}', studentId, state, content);
       if (!isStateValid(state)) reject(BAD_REQUEST);
-      console.log(content);
       let nextState: string, contentHolder: string;
       let valid = false;
       switch (state) {
diff --git a/src/people/dao/people.dao.ts b/src/people/dao/people.dao.ts
index 33fd035..bb03a1d 100644
--- a/src/people/dao/people.dao.ts
+++ b/src/people/dao/people.dao.ts
@@ -12,6 +12,8 @@ import { People } from '../schemas/people.schema';
 import * as Mailgun from 'mailgun-js';
 import config from 'src/config';
 import * as bcrypt from 'bcrypt';
+import { PeopleEntity } from '../entities/people.entity';
+import { CONFLICT } from 'src/shared/HttpError';
 
 @Injectable()
 export class PeopleDao {
@@ -73,6 +75,41 @@ export class PeopleDao {
     });
   };
 
+  saveMany = async (people: CreatePeopleDto[]): Promise<PeopleEntity[]> =>
+    Promise.all(
+      people.map(
+        (person): Promise<PeopleEntity> =>
+          new Promise((resolve, reject) => {
+            this._peopleModel.findOne(
+              { email: person.email },
+              {},
+              {},
+              (err, value) => {
+                if (err) {
+                  reject(err.message);
+                } else if (!value) {
+                  // Person does not exist -> create password + add to database
+                  this.secret().then((value) => {
+                    person.passwordHash = value;
+                    new this._peopleModel(person).save((err, value) => {
+                      if (err) reject(err.message);
+                      if (!value) reject(new InternalServerErrorException());
+                      this.sendPassword(person.email, person.passwordHash);
+                      resolve(value);
+                    });
+                  });
+                } else {
+                  resolve(value);
+                }
+              },
+            );
+          }),
+      ),
+    );
+  // Check for existing people in database based on email
+  // Add missing people
+  // Get all added people as PeopleEntity to have access to IDs
+
   findByIdAndUpdate = (
     id: string,
     people: UpdatePeopleDto,
diff --git a/src/people/dto/create-people.dto.ts b/src/people/dto/create-people.dto.ts
index c26bd35..00d43b8 100644
--- a/src/people/dto/create-people.dto.ts
+++ b/src/people/dto/create-people.dto.ts
@@ -1,7 +1,6 @@
-import { IsBoolean, IsString, IsNotEmpty, IsOptional } from 'class-validator';
+import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
 
 export class CreatePeopleDto {
-
   @IsString()
   @IsNotEmpty()
   firstname: string;
@@ -16,8 +15,7 @@ export class CreatePeopleDto {
   @IsString()
   @IsNotEmpty()
   email: string;
-  
-  @IsNotEmpty()
-  role: number;
 
-}
\ No newline at end of file
+  @IsOptional()
+  role: number;
+}
diff --git a/src/people/entities/people.entity.ts b/src/people/entities/people.entity.ts
index 7f8bf24..38dc121 100644
--- a/src/people/entities/people.entity.ts
+++ b/src/people/entities/people.entity.ts
@@ -11,4 +11,4 @@ export class PeopleEntity {
   constructor(partial: Partial<People>) {
     Object.assign(this, partial);
   }
-}
\ No newline at end of file
+}
diff --git a/src/people/people.controller.ts b/src/people/people.controller.ts
index 0837bc2..68b8d75 100644
--- a/src/people/people.controller.ts
+++ b/src/people/people.controller.ts
@@ -29,7 +29,7 @@ export class PeopleController {
   constructor(private readonly _peopleService: PeopleService) {}
 
 
-  @UseGuards(AuthGuard('jwt'))
+  //@UseGuards(AuthGuard('jwt'))
   @Get()
   findAll(): Promise<PeopleEntity[] | void> {
     return this._peopleService.findAll();
diff --git a/src/people/people.service.ts b/src/people/people.service.ts
index 2c35819..10d3195 100644
--- a/src/people/people.service.ts
+++ b/src/people/people.service.ts
@@ -24,6 +24,10 @@ export class PeopleService {
     return this._peopleDao.save(people);
   }
 
+  createMany = (people: CreatePeopleDto[]): Promise<PeopleEntity[]> => {
+    return this._peopleDao.saveMany(people);
+  };
+
   update = (
     id: string,
     people: UpdatePeopleDto,
diff --git a/src/shared/Roles.ts b/src/shared/Roles.ts
index 317633e..7911808 100644
--- a/src/shared/Roles.ts
+++ b/src/shared/Roles.ts
@@ -12,3 +12,18 @@ export const ROLES = [
 
 export const isRoleValid = (potentialRole: string): boolean =>
   ROLES.includes(potentialRole);
+
+export const roleValue = (role: string): number => {
+  switch (role) {
+    case ROLE_STUDENT:
+      return 0;
+    case ROLE_SECRETARY:
+      return 1;
+    case ROLE_RESPONSIBLE:
+      return 2;
+    case ROLE_ADMIN:
+      return 9999;
+    default:
+      return -1;
+  }
+};
-- 
GitLab