diff --git a/docker-compose.yml b/docker-compose.yml index e28e86d0f70ccc389b2cf30f2424f71a81ecfc20..3cfa17557f6da51eae4fb2dce5036a338d46975e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '2' services: service-ram: - image: registry.forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server:master + image: registry.forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server:dev ports: - ${SERVICE_API_BIND_PORT}:3000 extra_hosts: diff --git a/src/mailer/mail-templates/adminStructureClaim.ejs b/src/mailer/mail-templates/adminStructureClaim.ejs index 1e7e162f39e671fdf5d6beb2e1765a7d284e37e5..5571747d3e182b276244d05814914916f66d5036 100644 --- a/src/mailer/mail-templates/adminStructureClaim.ejs +++ b/src/mailer/mail-templates/adminStructureClaim.ejs @@ -1,4 +1,11 @@ Bonjour,<br /> <br /> -Une nouvelle structure a été revendiquée. Pour valider ou refuser la demande, merci de vous rendre sur +La structure <%= structureName %> à été revendiquée par <%= user.name %> <%= user.surname %>. +<br /> +Voici les informations de la structure : <br /> +<%= structureAdress %><br /> +<%= structureDescription %><br /> +Et du demandeur : <br /> +<%= user.email %><br /> +Pour valider ou refuser la demande, merci de vous rendre sur <a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/admin">ce lien</a>. diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts index d5eb2c1eb593a6d49db7b508164aa3168d9d8994..b58185d9d840c3c2e5ce30963d07fb5647ac879f 100644 --- a/src/structures/services/structures.service.ts +++ b/src/structures/services/structures.service.ts @@ -430,7 +430,8 @@ export class StructuresService { public async sendAdminStructureNotification( structure: StructureDocument, templateLocation: any, - jsonConfigLocation: any + jsonConfigLocation: any, + user: any = null ) { const uniqueAdminEmails = [...new Set((await this.userService.getAdmins()).map((admin) => admin.email))].map( (item) => { @@ -438,14 +439,21 @@ export class StructuresService { } ); + //ici récupérer le user Actuel avec le service afin de remplir le mail. const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(templateLocation); const jsonConfig = this.mailerService.loadJsonConfig(jsonConfigLocation); - const html = await ejs.renderFile(ejsPath, { config, id: structure ? structure._id : 0, structureName: structure ? structure.structureName : '', + structureAdress: structure + ? structure.address.numero + ? `${structure.address.numero} ${structure.address.street} ${structure.address.commune}` + : `${structure.address.street} ${structure.address.commune}` + : '', + structureDescription: structure ? structure.otherDescription : '', + user: user, }); this.mailerService.send(uniqueAdminEmails, jsonConfig.subject, html); } diff --git a/src/structures/structures.controller.spec.ts b/src/structures/structures.controller.spec.ts index 1eee0eaaa2d68dcb9b33924f6c2c58ad684808e2..6762f14c34fca751913f7ded03c94dff3fcf3b96 100644 --- a/src/structures/structures.controller.spec.ts +++ b/src/structures/structures.controller.spec.ts @@ -12,6 +12,7 @@ import { CategoriesFormationsService } from '../categories/services/categories-f import { CategoriesOthersService } from '../categories/services/categories-others.service'; import { TempUserService } from '../temp-user/temp-user.service'; import { UsersService } from '../users/users.service'; +import { CreateStructureDto } from './dto/create-structure.dto'; import { StructuresService } from './services/structures.service'; import { StructuresController } from './structures.controller'; describe('AuthController', () => { @@ -60,8 +61,178 @@ describe('AuthController', () => { expect(controller).toBeDefined(); }); + it('should get structure coordinates', async () => { + const coords = controller.getCoordinates('Lyon'); + expect(coords).toBeTruthy(); + }); + + it('should create structure', async () => { + const structure: CreateStructureDto = { + idUser: '1', + structure: null, + }; + const res = controller.create(structure); + expect(res).toBeTruthy(); + }); + + it('should update structure after ownerVerify', async () => { + const structureId = '1'; + const res = controller.updateAfterOwnerVerify(structureId); + expect(res).toBeTruthy(); + }); + + it('should update structure', async () => { + const structureService = new StructuresServiceMock(); + const structure = structureService.findOne('6093ba0e2ab5775cfc01ed3e'); + + const structureId = '1'; + const res = await controller.update(structureId, { + numero: null, + deletedAt: null, + remoteAccompaniment: null, + ...structure, + }); + expect(res.structureName).toBe('a'); + }); + + it('should get all structure', async () => { + const res = await controller.findAll(); + expect(res.length).toBe(2); + }); + + it('should get all Formated structure', async () => { + const res = await controller.findAllFormated(); + expect(res.length).toBe(2); + }); + + it('should search structure', async () => { + const res = controller.search(null, null); + expect(res).toBeTruthy(); + }); + + it('should reset Search Index', async () => { + const res = controller.resetES(); + expect(res).toBeTruthy(); + }); + + it('should see if structure is claimed', async () => { + const res = controller.isClaimed('1'); + expect(res).toBeTruthy(); + }); + + it('should claim structure', async () => { + const userMock = new UsersServiceMock(); + const user = userMock.findOne('pauline.dupont@mii.com'); + const res = controller.claim('1', { + phone: null, + resetPasswordToken: null, + changeEmailToken: null, + newEmail: null, + pendingStructuresLink: null, + structuresLink: null, + structureOutdatedMailSent: null, + email: user.email, + name: user.name, + surname: user.surname, + emailVerified: true, + password: user.password, + validationToken: null, + role: null, + }); + expect(res).toBeTruthy(); + }); + + it('should count', async () => { + const res = controller.countCategories([{ id: 'equipmentsAndServices', text: 'wifiEnAccesLibre' }]); + expect(res).toBeTruthy(); + }); + + it('should search an address', async () => { + const res = controller.searchAddress({ searchQuery: 'Rue Alphonse Daudet' }); + expect(res).toBeTruthy(); + }); + + it('should find struct', async () => { + let res = controller.find('6093ba0e2ab5775cfc01ed3e'); + expect(res).toBeTruthy(); + res = controller.find(''); + expect(res).toBeTruthy(); + }); + + it('should find struct with owners', async () => { + const res = controller.findWithOwners('6093ba0e2ab5775cfc01ed3e', { emailUser: 'pauline.dupont@mii.com' }); + expect(res).toBeTruthy(); + }); + + it('should delete struct', async () => { + const res = controller.delete('6093ba0e2ab5775cfc01ed3e'); + expect(res).toBeTruthy(); + }); + + it('should add Owner', async () => { + let res = controller.addOwner('6093ba0e2ab5775cfc01ed3e', { email: 'pauline.dupont@mii.com' }); + expect(res).toBeTruthy(); + res = controller.addOwner('6093ba0e2ab5775cfc01ed3e', { email: 'pauline.dupont@mii.fr' }); + expect(res).toBeTruthy(); + res = controller.addOwner('', { email: 'pauline.dupont@mii.fr' }); + expect(res).toBeTruthy(); + }); + + it('should remove Owner', async () => { + let res = controller.removeOwner('6093ba0e2ab5775cfc01ed3e', 'tsfsf6296'); + expect(res).toBeTruthy(); + res = controller.removeOwner('6093ba0e2ab5775cfc01ed3e', '1'); + expect(res).toBeTruthy(); + res = controller.removeOwner('', '1'); + expect(res).toBeTruthy(); + }); + + it('should join user', async () => { + const userMock = new UsersServiceMock(); + const user = userMock.findOne('pauline.dupont@mii.com'); + let res = controller.join('6093ba0e2ab5775cfc01ed3e', { + phone: null, + resetPasswordToken: null, + changeEmailToken: null, + newEmail: null, + pendingStructuresLink: null, + structuresLink: null, + structureOutdatedMailSent: null, + email: user.email, + name: user.name, + surname: user.surname, + emailVerified: true, + password: user.password, + validationToken: null, + role: null, + }); + expect(res).toBeTruthy(); + res = controller.join('', null); + expect(res).toBeTruthy(); + res = controller.join('6093ba0e2ab5775cfc01ed3e', null); + expect(res).toBeTruthy(); + }); + + it('should join in struct', async () => { + let res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'true', 'tsfsf6296'); + expect(res).toBeTruthy(); + res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'true', ''); + expect(res).toBeTruthy(); + res = controller.joinValidation('', 'true', ''); + expect(res).toBeTruthy(); + }); + + it('should remove user from struct', async () => { + const res = controller.joinValidation('6093ba0e2ab5775cfc01ed3e', 'false', 'tsfsf6296'); + expect(res).toBeTruthy(); + }); + + it('should report any structure error', async () => { + const res = controller.reportStructureError({ structureId: '6093ba0e2ab5775cfc01ed3e', content: null }); + expect(res).toBeTruthy(); + }); + //TODO: test structure controler endpoint - //create, search, updateAccountVerified, update, findAll, findAllFormated, isClaimed - //updateStructureLinkedClaim, countCategories, searchAddress, find, findWithOwners - //delete, addOwner, join, joinValidation, removeOwner, reportStructureError + //updateAccountVerified, + //updateStructureLinkedClaim }); diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts index 65dcfd00b0b4d5495ce84306e940f99a82982981..8d313a822ff90e89b1cf4cb8ddd9f68373424e99 100644 --- a/src/structures/structures.controller.ts +++ b/src/structures/structures.controller.ts @@ -108,7 +108,8 @@ export class StructuresController { @Post(':id/claim') public async claim(@Param('id') idStructure: string, @Body() user: User): Promise<Types.ObjectId[]> { - return this.userService.updateStructureLinkedClaim(user.email, idStructure); + const structure = await this.structureService.findOne(idStructure); + return this.userService.updateStructureLinkedClaim(user.email, idStructure, structure); } @Post('count') diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 6f513374851bfd2b405d920bf55e78b56cc5bd5e..6830094ce1b7bc71510a2a2fc597cdfb9e824eaf 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -40,11 +40,13 @@ export class UsersController { } const user = await this.usersService.create(createUserDto); if (structureId) { - this.usersService.updateStructureLinkedClaim(createUserDto.email, structureId); + const structure = await this.structureService.findOne(structureId); + this.usersService.updateStructureLinkedClaim(createUserDto.email, structureId, structure); this.structureService.sendAdminStructureNotification( null, this.configurationService.config.templates.adminStructureClaim.ejs, - this.configurationService.config.templates.adminStructureClaim.json + this.configurationService.config.templates.adminStructureClaim.json, + user ); } // Remove temp user if exist diff --git a/src/users/users.module.ts b/src/users/users.module.ts index 6a481a9e123a681074b40daac020f3b9244c2d65..87795896f53bdd466a281e6e15d6441a787e60e6 100644 --- a/src/users/users.module.ts +++ b/src/users/users.module.ts @@ -6,6 +6,7 @@ import { User, UserSchema } from './schemas/user.schema'; import { MailerModule } from '../mailer/mailer.module'; import { StructuresModule } from '../structures/structures.module'; import { TempUserModule } from '../temp-user/temp-user.module'; + @Module({ imports: [ MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 3782bd1d87544613dd9afe5a7915d220642bf77d..f44e5f52c246e74d832facc5a1239883b2ecaddc 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -12,6 +12,7 @@ import { IUser } from './interfaces/user.interface'; import { EmailChangeDto } from './dto/change-email.dto'; import { PendingStructureDto } from '../admin/dto/pending-structure.dto'; import { OwnerDto } from './dto/owner.dto'; +import { StructureDocument } from '../structures/schemas/structure.schema'; @Injectable() export class UsersService { @@ -317,13 +318,22 @@ export class UsersService { * Send to all admins validation email for structures * new account. */ - private async sendAdminStructureValidationMail(): Promise<any> { + private async sendAdminStructureValidationMail(userEmail: string, structure: StructureDocument): Promise<any> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.adminStructureClaim.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.adminStructureClaim.json); - + const user = await this.findOne(userEmail); const html = await ejs.renderFile(ejsPath, { config, + id: structure ? structure._id : 0, + structureName: structure ? structure.structureName : '', + structureAdress: structure + ? structure.address.numero + ? `${structure.address.numero} ${structure.address.street} ${structure.address.commune}` + : `${structure.address.street} ${structure.address.commune}` + : '', + structureDescription: structure ? structure.otherDescription : '', + user: user, }); const admins = await this.getAdmins(); admins.forEach((admin) => { @@ -331,9 +341,13 @@ export class UsersService { }); } - public async updateStructureLinkedClaim(userEmail: string, idStructure: string): Promise<Types.ObjectId[]> { + public async updateStructureLinkedClaim( + userEmail: string, + idStructure: string, + structure: StructureDocument + ): Promise<Types.ObjectId[]> { const stucturesLinked = this.updatePendingStructureLinked(userEmail, idStructure); - this.sendAdminStructureValidationMail(); + this.sendAdminStructureValidationMail(userEmail, structure); return stucturesLinked; } diff --git a/test/mock/services/structures.mock.service.ts b/test/mock/services/structures.mock.service.ts index a3432c034ab8ecb859199518b728d59adf6b5c8c..f088613ba024969b276d4998118a707852e7240d 100644 --- a/test/mock/services/structures.mock.service.ts +++ b/test/mock/services/structures.mock.service.ts @@ -635,4 +635,234 @@ export class StructuresServiceMock { }, ]; } + + findAllFormated() { + return [ + { + _id: '6093ba0e2ab5775cfc01abcd', + coord: [4.8498155, 45.7514817], + equipmentsAndServices: ['wifiEnAccesLibre'], + digitalCultureSecurity: [], + parentingHelp: [], + socialAndProfessional: [], + accessRight: [], + baseSkills: [], + proceduresAccompaniment: [], + publicsAccompaniment: [], + publics: ['adultes'], + labelsQualifications: [], + accessModality: ['telephoneVisio'], + structureType: null, + structureName: 'a', + description: null, + lockdownActivity: null, + address: { + numero: null, + street: 'Rue Alphonse Daudet', + commune: 'Lyon 7ème Arrondissement', + }, + contactMail: '', + contactPhone: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + linkedin: null, + hours: { + monday: { + open: false, + time: [], + }, + tuesday: { + open: false, + time: [], + }, + wednesday: { + open: false, + time: [], + }, + thursday: { + open: false, + time: [], + }, + friday: { + open: false, + time: [], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + pmrAccess: false, + exceptionalClosures: null, + otherDescription: null, + nbComputers: 1, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + nbScanners: 1, + freeWorkShop: false, + accountVerified: true, + createdAt: '2021-05-06T09:42:38.000Z', + updatedAt: '2021-05-06T09:42:50.000Z', + __v: 0, + }, + { + _id: '6093ba0e2ab5775cfc01ffff', + coord: [4.8498155, 45.7514817], + equipmentsAndServices: ['wifiEnAccesLibre'], + digitalCultureSecurity: [], + parentingHelp: [], + socialAndProfessional: [], + accessRight: [], + baseSkills: [], + proceduresAccompaniment: [], + publicsAccompaniment: [], + publics: ['adultes'], + labelsQualifications: [], + accessModality: ['telephoneVisio'], + structureType: null, + structureName: 'a', + description: null, + lockdownActivity: null, + address: { + numero: null, + street: 'Rue Alphonse Daudet', + commune: 'Lyon 7ème Arrondissement', + }, + contactMail: '', + contactPhone: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + linkedin: null, + hours: { + monday: { + open: false, + time: [], + }, + tuesday: { + open: false, + time: [], + }, + wednesday: { + open: false, + time: [], + }, + thursday: { + open: false, + time: [], + }, + friday: { + open: false, + time: [], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + pmrAccess: false, + exceptionalClosures: null, + otherDescription: null, + nbComputers: 1, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + nbScanners: 1, + freeWorkShop: false, + accountVerified: true, + createdAt: '2021-05-06T09:42:38.000Z', + updatedAt: '2021-05-06T09:42:50.000Z', + __v: 0, + }, + ]; + } + + update() { + return { + _id: '6093ba0e2ab5775cfc01ed3e', + coord: [4.8498155, 45.7514817], + equipmentsAndServices: ['wifiEnAccesLibre'], + digitalCultureSecurity: [], + parentingHelp: [], + socialAndProfessional: [], + accessRight: [], + baseSkills: [], + proceduresAccompaniment: [], + publicsAccompaniment: [], + publics: ['adultes'], + labelsQualifications: [], + accessModality: ['telephoneVisio'], + structureType: null, + structureName: 'a', + description: null, + lockdownActivity: null, + address: { + numero: null, + street: 'Rue Alphonse Daudet', + commune: 'Lyon 7ème Arrondissement', + }, + contactMail: '', + contactPhone: '', + website: '', + facebook: null, + twitter: null, + instagram: null, + linkedin: null, + hours: { + monday: { + open: false, + time: [], + }, + tuesday: { + open: false, + time: [], + }, + wednesday: { + open: false, + time: [], + }, + thursday: { + open: false, + time: [], + }, + friday: { + open: false, + time: [], + }, + saturday: { + open: false, + time: [], + }, + sunday: { + open: false, + time: [], + }, + }, + pmrAccess: false, + exceptionalClosures: null, + otherDescription: null, + nbComputers: 1, + nbPrinters: 1, + nbTablets: 1, + nbNumericTerminal: 1, + nbScanners: 1, + freeWorkShop: false, + accountVerified: true, + createdAt: '2021-05-06T09:42:38.000Z', + updatedAt: '2021-05-06T09:42:50.000Z', + __v: 0, + }; + } } diff --git a/test/mock/services/tempUser.mock.service.ts b/test/mock/services/tempUser.mock.service.ts index 8769fa94f3b52596b1a366312475df0f08b05830..4986277f3125fb1add58f8bbd3fa065d09ca47c8 100644 --- a/test/mock/services/tempUser.mock.service.ts +++ b/test/mock/services/tempUser.mock.service.ts @@ -1 +1,39 @@ -export class TempUserServiceMock {} +export class TempUserServiceMock { + findOne(mail: string, passwordQuery?: boolean) { + if (mail === 'pauline.dupont@mii.com') { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: true, + email: 'pauline.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + name: 'DUPONT', + surname: 'Pauline', + }; + } + if (mail === 'jacques.dupont@mii.com') { + if (passwordQuery) { + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: false, + email: 'jacques.dupont@mii.com', + password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu', + role: 0, + }; + } + return { + _id: 'tsfsf6296', + validationToken: + 'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42', + emailVerified: false, + email: 'jacques.dupont@mii.com', + role: 0, + }; + } + return null; + } +}