diff --git a/.eslintrc.js b/.eslintrc.js index 586a558b273d1ea6cf8fe8a01512e358ab2270fe..fb5cdea316180704431b8f086a3e3d6deec53ac6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,6 @@ module.exports = { '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-explicit-any': 'warn', }, }; diff --git a/.vscode/settings.json b/.vscode/settings.json index b699aca824a1d10f95332b91d4872f341fac33f9..16e8d9211f1cd0e182bf21ab15300145211aff09 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,41 +1,42 @@ { - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#b299e5", - "activityBar.activeBorder": "#f9ece6", - "activityBar.background": "#b299e5", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#f9ece6", - "activityBarBadge.foreground": "#15202b", - "statusBar.background": "#9370db", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#7447d1", - "titleBar.activeBackground": "#9370db", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#9370db99", - "titleBar.inactiveForeground": "#15202b99" - }, - "peacock.color": "#9370DB", - "javascript.format.enable": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - "eslint.validate": ["typescript"], - "[html]": { + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#b299e5", + "activityBar.activeBorder": "#f9ece6", + "activityBar.background": "#b299e5", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#f9ece6", + "activityBarBadge.foreground": "#15202b", + "statusBar.background": "#9370db", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#7447d1", + "titleBar.activeBackground": "#9370db", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#9370db99", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#9370DB", + "javascript.format.enable": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "eslint.validate": ["typescript"], + "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[javascript]": { + }, + "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { + }, + "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[scss]": { + }, + "[scss]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { + }, + "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "editor.formatOnSave": true, - "typescript.preferences.importModuleSpecifier": "relative" -} \ No newline at end of file + }, + "editor.formatOnSave": true, + "typescript.preferences.importModuleSpecifier": "relative", + "cSpell.words": ["aptic", "cnfs", "grandlyon", "nestjs", "photonban"] +} diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts index 983783bbb9b7660a4c87b3806187bc17d53216de..0a9bf0cd9e6ea35381780f2e67de77cc8f77b256 100644 --- a/src/admin/admin.controller.ts +++ b/src/admin/admin.controller.ts @@ -62,7 +62,7 @@ export class AdminController { @Roles('admin') @Get('adminStructuresList') @ApiOperation({ description: 'Get pending structures for validation' }) - public async getAdminStructuresList(): Promise<any> { + public async getAdminStructuresList() { this.logger.debug('getAdminStructuresList'); const structuresList = { claimed: [], inClaim: [], toClaim: [], incomplete: [] }; const inClaimStructures = await this.getPendingAttachments(); diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 93a6e6288c59935dc07b42bf3e2798a37542391e..b84d9d146871a6c631cb69a3c7301fab82cc01e1 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body } from '@nestjs/common'; +import { Body, Controller, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { LoginDto } from './login-dto'; @@ -9,7 +9,7 @@ export class AuthController { constructor(private authService: AuthService) {} @Post('login') - async login(@Body() loginDto: LoginDto): Promise<{ username; name; surname; token }> { + async login(@Body() loginDto: LoginDto) { return this.authService.login(loginDto); } } diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts index f67d73a15813068167a6ead2b7049101614517bc..134ae6351a8f5b607495244ce72f0c1458c2b1b1 100644 --- a/src/auth/auth.service.spec.ts +++ b/src/auth/auth.service.spec.ts @@ -57,6 +57,10 @@ describe('AuthService', () => { }); describe('login', () => { + let _createToken; + beforeAll(() => { + _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); + }); it('should login user pauline.dupont@mii.com', async () => { // Token creation mock const token = { @@ -64,7 +68,6 @@ describe('AuthService', () => { 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', expiresAt: '2021-05-04T15:35:06.663+02:00', }; - const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); _createToken.mockImplementation(() => token); const loginDto: LoginDto = { email: 'pauline.dupont@mii.com', password: 'test1A!!' }; //NOSONAR @@ -85,7 +88,6 @@ describe('AuthService', () => { 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', expiresAt: '2021-05-04T15:35:06.663+02:00', }; - const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); _createToken.mockImplementation(() => token); const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR @@ -105,7 +107,6 @@ describe('AuthService', () => { 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', expiresAt: '2021-05-04T15:35:06.663+02:00', }; - const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); _createToken.mockImplementation(() => token); const loginDto: LoginDto = { email: 'toto@mii.com', password: 'test1A!!' }; //NOSONAR @@ -125,7 +126,6 @@ describe('AuthService', () => { 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4', expiresAt: '2021-05-04T15:35:06.663+02:00', }; - const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken'); _createToken.mockImplementation(() => token); const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: '1' }; //NOSONAR diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index d1419471c8f7cfa30b5451bc50ccd118a2cbd4e9..818dac0fc413fae3382e02613548e746d42f0170 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,15 +1,16 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { UsersService } from '../users/services/users.service'; import { JwtService } from '@nestjs/jwt'; -import { LoginDto } from './login-dto'; import { DateTime } from 'luxon'; +import { IUser } from '../users/interfaces/user.interface'; import { User } from '../users/schemas/user.schema'; +import { UsersService } from '../users/services/users.service'; +import { LoginDto } from './login-dto'; @Injectable() export class AuthService { constructor(private usersService: UsersService, private jwtService: JwtService) {} - async validateUser(loginDto: LoginDto): Promise<any> { + async validateUser(loginDto: LoginDto): Promise<IUser> { const user = await this.usersService.findOne(loginDto.email); if (user) { return user; @@ -17,7 +18,7 @@ export class AuthService { return null; } - async login(loginDto: LoginDto): Promise<{ username; name; surname; token }> { + async login(loginDto: LoginDto) { // find user in db const user: User = await this.usersService.findByLogin(loginDto); if (!user.emailVerified) { @@ -35,7 +36,7 @@ export class AuthService { }; } - private _createToken(user: User): any { + private _createToken(user: User) { const local = DateTime.local().setZone('Europe/Paris'); return { accessToken: this.jwtService.sign({ email: user.email, role: user.role }), diff --git a/src/auth/strategy/jwt.strategy.ts b/src/auth/strategy/jwt.strategy.ts index 78f08c9e13f0983d63c2aa94b755a603a48ca60b..46d50b14f25eecd3c913c67a3f58e7056dca8721 100644 --- a/src/auth/strategy/jwt.strategy.ts +++ b/src/auth/strategy/jwt.strategy.ts @@ -1,6 +1,6 @@ -import { ExtractJwt, Strategy } from 'passport-jwt'; -import { PassportStrategy } from '@nestjs/passport'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; import { AuthService } from '../auth.service'; @Injectable() @@ -13,7 +13,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) { }); } - async validate(payload: any) { + async validate(payload) { const user = await this.authService.validateUser(payload); if (!user) { throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); diff --git a/src/contact/contact.controller.ts b/src/contact/contact.controller.ts index df137b3b85e2e9b3adee27820f4ae5b383a3cbc2..c205821824933708eddaf41f32a2f1fe1b4293f6 100644 --- a/src/contact/contact.controller.ts +++ b/src/contact/contact.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Post } from '@nestjs/common'; -import { ContactMessage } from './schemas/contact-message.schema'; -import { ContactService } from './contact.service'; import { ApiTags } from '@nestjs/swagger'; +import { ContactService } from './contact.service'; +import { ContactMessage } from './schemas/contact-message.schema'; @ApiTags('contact') @Controller('contact') @@ -9,7 +9,7 @@ export class ContactController { constructor(private contactService: ContactService) {} @Post('message') - public async sendContactMessage(@Body() data: { contactMessage: ContactMessage }): Promise<any> { + public async sendContactMessage(@Body() data: { contactMessage: ContactMessage }): Promise<unknown> { return this.contactService.sendMessage(data.contactMessage); } } diff --git a/src/contact/contact.service.spec.ts b/src/contact/contact.service.spec.ts index eb76c3239d8156537ddefa40d78bd6cdbde1fa9f..c8558d105cac8fae27b7745225072f52a064f36d 100644 --- a/src/contact/contact.service.spec.ts +++ b/src/contact/contact.service.spec.ts @@ -1,12 +1,12 @@ +import { HttpModule } from '@nestjs/axios'; import { HttpStatus } from '@nestjs/common'; -import { ContactMessage } from './schemas/contact-message.schema'; import { Test, TestingModule } from '@nestjs/testing'; -import { MailerModule } from '../mailer/mailer.module'; -import { ContactService } from './contact.service'; -import { MailerService } from '../mailer/mailer.service'; import { MailerMockService } from '../../test/mock/services/mailer.mock.service'; -import { HttpModule } from '@nestjs/axios'; import { ConfigurationService } from '../configuration/configuration.service'; +import { MailerModule } from '../mailer/mailer.module'; +import { MailerService } from '../mailer/mailer.service'; +import { ContactService } from './contact.service'; +import { ContactMessage } from './schemas/contact-message.schema'; describe('ContactService', () => { let service: ContactService; @@ -25,7 +25,7 @@ describe('ContactService', () => { }); it('should send message with status OK', async () => { - const res = await service.sendMessage(new ContactMessage()); + const res = (await service.sendMessage(new ContactMessage())) as { data: { status: unknown } }; expect(res.data.status).toBe(HttpStatus.OK); }); }); diff --git a/src/contact/contact.service.ts b/src/contact/contact.service.ts index 6080e3cc4cba139ab9f43d1ee54a9a7a766bc7fc..c9ad640cf0a8089b2f24d8d2d50656a5502f9fb0 100644 --- a/src/contact/contact.service.ts +++ b/src/contact/contact.service.ts @@ -1,14 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { MailerService } from '../mailer/mailer.service'; -import { ContactMessage } from './schemas/contact-message.schema'; import * as ejs from 'ejs'; import * as sanitizeHtml from 'sanitize-html'; +import { MailerService } from '../mailer/mailer.service'; +import { ContactMessage } from './schemas/contact-message.schema'; @Injectable() export class ContactService { constructor(private readonly mailerService: MailerService) {} - public async sendMessage(contactMessage: ContactMessage): Promise<any> { + public async sendMessage(contactMessage: ContactMessage): Promise<unknown> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.contactMessage.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.contactMessage.json); diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts index 549310ea8d619eb30a10e1ca39ed44d7b911f461..364f16cde92a0b856e9abadb9e71882beba4d24b 100644 --- a/src/mailer/mailer.service.ts +++ b/src/mailer/mailer.service.ts @@ -1,9 +1,9 @@ import { HttpService } from '@nestjs/axios'; import { Injectable, Logger } from '@nestjs/common'; import { AxiosResponse } from 'axios'; +import * as FormData from 'form-data'; import * as fs from 'fs'; import * as path from 'path'; -import * as FormData from 'form-data'; import { ConfigurationService } from '../configuration/configuration.service'; @Injectable() export class MailerService { @@ -12,23 +12,11 @@ export class MailerService { this.config = this.configurationService.config; } - /** - * Send an email - * - * @param {string} from - * @param {string} to - * @param {string} replyTo - * @param {string} subject - * @param {string} html - * @param {string} text - */ - public async send(to: string | { email: string }[], subject: string, html: string): Promise<AxiosResponse<any>> { + public async send(to: string | { email: string }[], subject: string, html: string): Promise<AxiosResponse<unknown>> { const emailsToSend = typeof to === 'string' ? [{ email: to }] : to; const formData = new FormData(); const data = JSON.stringify({ - // eslint-disable-next-line camelcase from_email: this.config.from, - // eslint-disable-next-line camelcase from_name: this.config.from_name, to: emailsToSend, reply_to: 'inclusionnumerique@grandlyon.com', @@ -62,10 +50,8 @@ export class MailerService { /** * Get email template location from config directory and given filename. Also, check if file exists. - * - * @param {string} filename */ - public getTemplateLocation(filename) { + public getTemplateLocation(filename: string) { const ejsPath = path.join(this.config.templates.directory, filename); if (!fs.existsSync(ejsPath)) { throw new Error(`Email template '${filename}' cannot be found in ${this.config.templates.directory}`); @@ -75,10 +61,8 @@ export class MailerService { /** * Load email json config file from config directory and given filename. Also, check if file exists - * - * @param {filename} filename */ - public loadJsonConfig(filename) { + public loadJsonConfig(filename: string) { const jsonPath = path.join(this.config.templates.directory, filename); if (!fs.existsSync(jsonPath)) { throw new Error(`Email json definition file '${filename}' cannot be found in ${this.config.templates.directory}`); diff --git a/src/personal-offers/personal-offers.service.spec.ts b/src/personal-offers/personal-offers.service.spec.ts index e5e4c885b188f80cc5a1bc61b40d326003be1f79..fba7491954d573b15cd0042c2032ba323e19317d 100644 --- a/src/personal-offers/personal-offers.service.spec.ts +++ b/src/personal-offers/personal-offers.service.spec.ts @@ -1,15 +1,15 @@ -import { StructuresServiceMock } from './../../test/mock/services/structures.mock.service'; -import { StructuresService } from './../structures/services/structures.service'; import { getModelToken } from '@nestjs/mongoose'; import { Test, TestingModule } from '@nestjs/testing'; import { createPersonalOffersDtoDataMock, - updatePersonalOffersDtoDataMock, personalOffersDataMock, + updatePersonalOffersDtoDataMock, } from '../../test/mock/data/personalOffers.mock.data'; -import { PersonalOffersService } from './personal-offers.service'; -import { UsersService } from '../users/services/users.service'; import { UsersServiceMock } from '../../test/mock/services/user.mock.service'; +import { UsersService } from '../users/services/users.service'; +import { StructuresServiceMock } from './../../test/mock/services/structures.mock.service'; +import { StructuresService } from './../structures/services/structures.service'; +import { PersonalOffersService } from './personal-offers.service'; describe('PersonalOffersService', () => { let service: PersonalOffersService; @@ -61,7 +61,7 @@ describe('PersonalOffersService', () => { it('should return exception if personal offer is not found for given id', async () => { personalOfferModelMock.findById.mockReturnThis(); personalOfferModelMock.exec.mockResolvedValueOnce(null); - let error: any; + let error; try { await service.findOne('abcd'); } catch (e) { @@ -90,7 +90,7 @@ describe('PersonalOffersService', () => { it('should return exception if personal offer is not found for given id', async () => { personalOfferModelMock.findById.mockReturnThis(); personalOfferModelMock.exec.mockResolvedValueOnce(null); - let error: any; + let error; try { await service.update('abcd', updatePersonalOffersDtoDataMock[1]); } catch (e) { @@ -110,7 +110,7 @@ describe('PersonalOffersService', () => { it('should return exception if personal offer is not found for given id', async () => { personalOfferModelMock.findById.mockReturnThis(); personalOfferModelMock.exec.mockResolvedValueOnce(null); - let error: any; + let error; try { await service.delete('abcd'); } catch (e) { diff --git a/src/posts/posts.service.ts b/src/posts/posts.service.ts index f76155787a74299c5f1cbe16b7bd1dbf8bccf645..76e750db9a55488bfe881c95a5598ba5c7773bf8 100644 --- a/src/posts/posts.service.ts +++ b/src/posts/posts.service.ts @@ -2,17 +2,17 @@ import { Injectable } from '@nestjs/common'; import * as GhostAdminAPI from '@tryghost/admin-api'; import * as _ from 'lodash'; import { ConfigurationService } from '../configuration/configuration.service'; +import { rewriteGhostImgUrl } from '../shared/utils'; import { TagEnum } from './enums/tag.enum'; import { Post } from './schemas/post.schema'; import { Tag } from './schemas/tag.schema'; -import { rewriteGhostImgUrl } from '../shared/utils'; @Injectable() export class PostsService { // ID's use in ghost tag description in order to categories tags. public readonly locationCategory = 'commune'; public readonly publicCategory = 'public'; - private api: any; + private api; constructor(private readonly configService: ConfigurationService) { // Configure Ghost client @@ -61,7 +61,7 @@ export class PostsService { return publicTags; } - public arraymove(arr, fromIndex, toIndex): Array<any> { + public arraymove(arr, fromIndex, toIndex): Array<unknown> { const element = arr[fromIndex]; arr.splice(fromIndex, 1); arr.splice(toIndex, 0, element); diff --git a/src/posts/schemas/pagination.schema.ts b/src/posts/schemas/pagination.schema.ts index 4f9ecaaec31d12a488d1ea219500d1fef26da007..b9984bbee5993d1520b889da9558f947c460a899 100644 --- a/src/posts/schemas/pagination.schema.ts +++ b/src/posts/schemas/pagination.schema.ts @@ -1,8 +1,8 @@ export class Pagination { limit: number; - next: any; page: number; pages: number; - prev: any; total: number; + prev: unknown; + next: unknown; } diff --git a/src/structures/services/aptic-structures.service.ts b/src/structures/services/aptic-structures.service.ts index 21dd98c8b5e9e68dfab88a0f13682746061fb57a..2174076315baefd2b917a8c3d3e1716ff8b9a0ec 100644 --- a/src/structures/services/aptic-structures.service.ts +++ b/src/structures/services/aptic-structures.service.ts @@ -70,7 +70,7 @@ export class ApticStructuresService { * Create a structure for app database given an aptic structure * @param structure ApticStructure */ - private async createApticStructures(structure: ApticStructure): Promise<any> { + private async createApticStructures(structure: ApticStructure): Promise<void> { this.structureAlreadyExist(structure).then(async (exist) => { if (!exist) { this.logger.log(`createApticStructures | Create structure : ${structure.name}`); @@ -247,7 +247,7 @@ export class ApticStructuresService { ); } - public getPostalCodeWithINSEE(inseeCode: string): Observable<AxiosResponse<any>> { + public getPostalCodeWithINSEE(inseeCode: string): Observable<AxiosResponse<unknown>> { const req = `https://geo.api.gouv.fr/communes/${inseeCode}?fields=codesPostaux&format=json`; this.logger.debug(`getMetropoleMunicipality | Request : ${req}`); return this.httpService.get(encodeURI(req)); diff --git a/src/structures/services/structures-search.service.spec.ts b/src/structures/services/structures-search.service.spec.ts index f20f6d682bd1f95c0dc92265251f95ae0f2e674b..da54a644bf008cc76503d3774ff4b5ba99cd9fd8 100644 --- a/src/structures/services/structures-search.service.spec.ts +++ b/src/structures/services/structures-search.service.spec.ts @@ -30,7 +30,7 @@ describe('StructuresSearchService', () => { const structuresForSearchService = new StructuresForSearchServiceMock(); const structures = structuresForSearchService.findAll(); await Promise.all( - structures.map((structure: any) => { + structures.map((structure) => { service.indexStructure(structure); }) ); diff --git a/src/structures/services/structures-search.service.ts b/src/structures/services/structures-search.service.ts index fd6aebc2ad32dd55837ad1c59a6e865cc3170ef2..810e1bf20442df031f1f8a1bd87dcf245e2723a7 100644 --- a/src/structures/services/structures-search.service.ts +++ b/src/structures/services/structures-search.service.ts @@ -1,4 +1,9 @@ -import { IndicesRefreshResponse, UpdateResponse } from '@elastic/elasticsearch/lib/api/types'; +import { + IndicesCreateResponse, + IndicesRefreshResponse, + IndicesResponseBase, + UpdateResponse, +} from '@elastic/elasticsearch/lib/api/types'; import { Injectable, Logger } from '@nestjs/common'; import { ElasticsearchService } from '@nestjs/elasticsearch'; import { escapeElasticsearchQuery, es_settings_homemade_french } from '../../shared/utils'; @@ -32,7 +37,7 @@ export class StructuresSearchService { }; } - public async createStructureIndex(): Promise<any> { + public async createStructureIndex(): Promise<IndicesCreateResponse> { this.logger.debug(`createStructureIndex`); // use custom analyzer with minimal_french stemmer to avoid default light_french stemmer problems (oullins -> oulin, "oull" not found) // don't use stopwords (le, la, de, son, etc.) for structureName (to find "maison de la", "le son du clic", etc.) @@ -63,7 +68,7 @@ export class StructuresSearchService { }); } - public async dropIndex(): Promise<any> { + public async dropIndex(): Promise<IndicesResponseBase> { this.logger.debug(`dropIndex`); if ( await this.elasticsearchService.indices.exists({ @@ -118,7 +123,7 @@ export class StructuresSearchService { return sortedHits.map((item) => item._source); } - public async update(structure: StructureDocument, id: string): Promise<UpdateResponse<any>> { + public async update(structure: StructureDocument, id: string): Promise<UpdateResponse<unknown>> { this.logger.debug('update'); return this.elasticsearchService.update({ index: this.index, diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts index afe382adbb7c61ea5f8452de229680d46bd46719..07f82e0f3f91275aaad01ea28c130684afc42685 100644 --- a/src/structures/services/structures.service.ts +++ b/src/structures/services/structures.service.ts @@ -146,7 +146,7 @@ export class StructuresService { this.mailerService.config.templates.adminStructureCreate.json ); - // Async treatment that can be done in backgorund, DO NOT await because it takes around 3sec to finish + // Async treatment that can be done in background, DO NOT await because it takes around 3sec to finish this.bindOneCNFSStructure(createdStructure.id); return createdStructure; @@ -268,7 +268,7 @@ export class StructuresService { * Find all structure and format them in order to be directly crawled */ public async findAllFormated(categories: Categories[]): Promise<StructureDocument[]> { - this.logger.debug('Find all formated structures, only returning structures who consented to data sharing'); + this.logger.debug('Find all formatted structures, only returning structures who consented to data sharing'); return ( await this.structureModel .find({ deletedAt: { $exists: false }, accountVerified: true, $where: 'this.dataShareConsentDate != null' }) @@ -494,12 +494,14 @@ export class StructuresService { */ public async searchAddress(data: { searchQuery: string; - }): Promise<{ features: { geometry: any; type: string; properties: any }[] }> { + }): Promise<{ features: { geometry: { coordinates: number[] }; type: string; properties: unknown }[] }> { const reqBan = `https://download.data.grandlyon.com/geocoding/photon/api?q=${data.searchQuery}&lang=fr&limit=500&osm_tag=:!construction&osm_tag=:!bus_stop`; const reqBal = `https://download.data.grandlyon.com/geocoding/photon-bal/api?q=${data.searchQuery}&lang=fr&limit=500&osm_tag=:!construction&osm_tag=:!bus_stop`; - const requestGroup = (url): Promise<{ features: { geometry: any; type: string; properties: any }[] }> => + const requestGroup = ( + url + ): Promise<{ features: { geometry: { coordinates: number[] }; type: string; properties: unknown }[] }> => new Promise((resolve) => { this.logger.debug(`Search request: ${encodeURI(url)}`, 'StructureService'); return this.httpService @@ -529,8 +531,8 @@ export class StructuresService { const mergedArray = [...reqBalRes['features'], ...reqBanRes['features']]; const duplicateFreeArray = _.unionWith(mergedArray, function (a, b) { return ( - // excludes structures from photon-BAN if they share postcode && street or name && housenumber - // checking for name and street existance asserts that we will not compare 2 undefined fields + // excludes structures from photon-BAN if they share postcode && street or name && house number + // checking for name and street existence asserts that we will not compare 2 undefined fields a.properties.postcode === b.properties.postcode && ((a.properties.name && (a.properties.name === b.properties.name || a.properties.name === b.properties.street)) || @@ -551,11 +553,11 @@ export class StructuresService { } /** - * Count every value occurence of a given key + * Count every value occurrence of a given key * @param key structure key * @return [{id: 'key', count: 'value'}] */ - public async countByStructureKey(key: string, selected: { id: string; text: string }[]): Promise<any> { + public async countByStructureKey(key: string, selected: { id: string; text: string }[]) { const uniqueElements = await this.structureModel.distinct(key).exec(); return Promise.all( uniqueElements.map(async (value) => { @@ -712,9 +714,9 @@ export class StructuresService { public async sendAdminStructureNotification( structure: StructureDocument, - templateLocation: any, - jsonConfigLocation: any, - user: any = null + templateLocation: string, + jsonConfigLocation: string, + user = null ) { const uniqueAdminEmails = [...new Set((await this.userService.getAdmins()).map((admin) => admin.email))].map( (item) => { @@ -773,9 +775,9 @@ export class StructuresService { .exec(); const pendingStructures = (await this.userService.getPendingStructures()) as PendingStructureDto[]; - const structurIds = pendingStructures.map((pending) => pending.structureId); + const structureIds = pendingStructures.map((pending) => pending.structureId); structures.forEach((structure) => { - if (structurIds.includes(structure.id)) { + if (structureIds.includes(structure.id)) { this.logger.debug(`cancel structure soft-delete : ${structure.structureName} (${structure._id})`); this.structureModel.findByIdAndUpdate(new Types.ObjectId(structure.id), { toBeDeletedAt: null, @@ -830,7 +832,7 @@ export class StructuresService { * a new account. * @param user User */ - private async sendOutdatedEmailToUser(userEmail: string, structureName: string, id: string): Promise<any> { + private async sendOutdatedEmailToUser(userEmail: string, structureName: string, id: string): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.structureOutdatedInfo.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.structureOutdatedInfo.json); @@ -939,7 +941,7 @@ export class StructuresService { return structure.contactMail === 'unknown@unknown.com'; } - public async getAllUserCompletedStructures(users: IUser[]): Promise<any[]> { + public async getAllUserCompletedStructures(users: IUser[]) { return Promise.all( users.map(async (user) => { return { @@ -985,7 +987,7 @@ export class StructuresService { * Add the personal offer to the structure * @param structureId string * @param personalOfferDocument PersonalOfferDocument - * @returns {Structure} structurew with personal offer added + * @returns {Structure} structure with personal offer added */ public async addPersonalOffer( structureId: string, @@ -1033,7 +1035,7 @@ export class StructuresService { } /** - * Compare Resin & CNFS structures with email and phone, returns the structure when mathing + * Compare Resin & CNFS structures with email and phone, returns the structure when matching */ public findMatchingStructure(resinStructure: StructureDocument, CNFSData: CNFSStructure[]): CNFSStructure | null { // This check is mandatory because if resin phone number is '' we should return FALSE instead of UNDEFINED @@ -1057,7 +1059,7 @@ export class StructuresService { const matchingStructure = this.findMatchingStructure(resinStructure, CNFSData); if (matchingStructure) { - // if a corresponing structure has been found but its email is different, we probably will need to update it here. + // if a corresponding structure has been found but its email is different, we probably will need to update it here. await this.setCNFSid(resinStructure.id, matchingStructure.id); return 'A match has been found, updating CNFSid'; } @@ -1077,7 +1079,7 @@ export class StructuresService { this.logger.debug(`${resinStructures.length} resin structures found`); let matchCount = 0; - let dismatchCount = 0; + let mismatchCount = 0; for (const resinStructure of resinStructures) { const matchingStructure = this.findMatchingStructure(resinStructure, CNFSData); @@ -1085,15 +1087,15 @@ export class StructuresService { if (await this.setCNFSid(resinStructure.id, matchingStructure.id)) { matchCount++; } - // if a corresponing structure has been found but its email is different, we probably will need to update it here. + // if a corresponding structure has been found but its email is different, we probably will need to update it here. } else { if (await this.setCNFSid(resinStructure.id, '')) { - dismatchCount++; + mismatchCount++; } } } this.logger.log( - `${matchCount} structures affected with a idCNFS (${dismatchCount} structures with idCNFS emptied)` + `${matchCount} structures affected with a idCNFS (${mismatchCount} structures with idCNFS emptied)` ); return `${matchCount} structures affected`; } diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts index 6b7e1304339022e7747bf5e557def4deccd487e3..b354a6821dedd1a68835b1760eda58db8bd5b3e1 100644 --- a/src/structures/structures.controller.ts +++ b/src/structures/structures.controller.ts @@ -16,6 +16,7 @@ import { UseGuards, } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; +import * as _ from 'lodash'; import { Types } from 'mongoose'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { CategoriesService } from '../categories/services/categories.service'; @@ -37,7 +38,6 @@ import { QueryStructure } from './dto/query-structure.dto'; import { UpdateStructureDto } from './dto/update-structure.dto'; import { Structure, StructureDocument } from './schemas/structure.schema'; import { StructuresService } from './services/structures.service'; -import * as _ from 'lodash'; @ApiTags('structures') @Controller('structures') @@ -59,7 +59,7 @@ export class StructuresController { */ @Get('coordinates/:zipcode') @ApiParam({ name: 'zipcode', type: String, required: true }) - public async getCoordinates(@Param('zipcode') city: string): Promise<any> { + public async getCoordinates(@Param('zipcode') city: string): Promise<unknown> { return this.httpService .get(encodeURI(`https://download.data.grandlyon.com/geocoding/photon/api?q=${city}`)) .toPromise() diff --git a/src/tcl/interface/BusLine.ts b/src/tcl/interface/BusLine.ts new file mode 100644 index 0000000000000000000000000000000000000000..eac0325ed66c80a96a151878d416e801cfddf880 --- /dev/null +++ b/src/tcl/interface/BusLine.ts @@ -0,0 +1,23 @@ +export interface BusLine { + code_ligne: string; + code_trace: string; + code_tri_ligne: string; + code_type_ligne: string; + date_debut: string; + date_fin: null; + destination: string; + famille_transport: string; + gid: number; + last_update: Date; + last_update_fme: Date; + ligne: string; + nom_destination: string; + nom_origine: string; + nom_trace: string; + nom_type_ligne: string; + nom_version: string; + origine: string; + pmr: boolean; + sens: string; + type_trace: string; +} diff --git a/src/tcl/interface/StopPoint.ts b/src/tcl/interface/StopPoint.ts new file mode 100644 index 0000000000000000000000000000000000000000..22a6f66d8b7da51d46f338ae86d8bd0637fcbcfc --- /dev/null +++ b/src/tcl/interface/StopPoint.ts @@ -0,0 +1,17 @@ +import { PgisCoord } from '../schemas/pgisCoord.schema'; + +export interface StopPoint { + type: string; + properties: { + id: string; + nom: string; + desserte: string; + pmr: string; + ascenseur: string; + escalator: string; + gid: string; + last_update: Date; + last_update_fme: Date; + }; + geometry: PgisCoord; +} diff --git a/src/tcl/interface/SubLine.ts b/src/tcl/interface/SubLine.ts new file mode 100644 index 0000000000000000000000000000000000000000..d55bcbc73eff1fe9f7a835cf4732a8f679ff8adf --- /dev/null +++ b/src/tcl/interface/SubLine.ts @@ -0,0 +1,24 @@ +export interface SubLine { + code_ligne: string; + code_trace: string; + code_tri_ligne: string; + code_type_ligne: string; + couleur: string; + date_debut: string; + date_fin: null; + destination: string; + famille_transport: string; + gid: number; + last_update: Date; + last_update_fme: Date; + ligne: string; + nom_destination: string; + nom_origine: string; + nom_trace: string; + nom_type_ligne: string; + nom_version: string; + origine: string; + pmr: boolean; + sens: string; + type_trace: string; +} diff --git a/src/tcl/interface/TramLine.ts b/src/tcl/interface/TramLine.ts new file mode 100644 index 0000000000000000000000000000000000000000..cdf6eea39c08d2cda2d35a5ca0e42a591cbcf522 --- /dev/null +++ b/src/tcl/interface/TramLine.ts @@ -0,0 +1,24 @@ +export interface TramLine { + code_ligne: string; + code_trace: string; + code_tri_ligne: string; + code_type_ligne: string; + couleur: string; + date_debut: string; + date_fin: null; + destination: string; + famille_transport: string; + gid: number; + last_update: Date; + last_update_fme: Date; + ligne: string; + nom_destination: string; + nom_origine: string; + nom_trace: string; + nom_type_ligne: string; + nom_version: string; + origine: string; + pmr: boolean; + sens: string; + type_trace: string; +} diff --git a/src/tcl/tclStopPoint.service.ts b/src/tcl/tclStopPoint.service.ts index 2a19b08ba3636812360589ffddcfe1bfbc2d1e24..30b8fcdf0ef22840841f3feda232220fa6d29da5 100644 --- a/src/tcl/tclStopPoint.service.ts +++ b/src/tcl/tclStopPoint.service.ts @@ -2,25 +2,13 @@ import { HttpService } from '@nestjs/axios'; import { Injectable, Logger } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; +import { BusLine } from './interface/BusLine'; +import { StopPoint } from './interface/StopPoint'; +import { SubLine } from './interface/SubLine'; +import { TramLine } from './interface/TramLine'; import { PgisCoord } from './schemas/pgisCoord.schema'; import { TclStopPoint, TclStopPointDocument } from './tclStopPoint.schema'; -interface ReceivedStopPoint { - type: string; - properties: { - id: string; - nom: string; - desserte: string; - pmr: string; - ascenseur: string; - escalator: string; - gid: string; - last_update: string; - last_update_fme: string; - }; - geometry: PgisCoord; -} - interface Lines { busLines: string[]; subLines: string[]; @@ -30,10 +18,10 @@ interface Lines { @Injectable() export class TclStopPointService { private readonly logger = new Logger(TclStopPointService.name); - private receivedStopPoints: any[]; - private receivedBusLines: any[]; - private receivedSubLines: any[]; - private receivedTramLines: any[]; + private receivedStopPoints: StopPoint[]; + private receivedBusLines: BusLine[]; + private receivedSubLines: SubLine[]; + private receivedTramLines: TramLine[]; constructor( private http: HttpService, @@ -84,7 +72,7 @@ export class TclStopPointService { /** * Get all lines names and remove duplications */ - private async processReceivedStopPoints(receivedStopPoints: ReceivedStopPoint[]): Promise<TclStopPoint[]> { + private async processReceivedStopPoints(receivedStopPoints: StopPoint[]): Promise<TclStopPoint[]> { const newStopPoints: TclStopPoint[] = []; for (const receivedStopPoint of receivedStopPoints) { @@ -177,7 +165,7 @@ export class TclStopPointService { /** * Get back bus line name from TCL code in the corresponding table */ - private async getCleanLine(line: string, receivedLines: any[]): Promise<string> { + private async getCleanLine(line: string, receivedLines: BusLine[] | TramLine[] | SubLine[]): Promise<string> { const foundLine = receivedLines.find((receivedLine) => receivedLine.code_ligne === line); // Exception for line 132. Does'nt exist anymore diff --git a/src/temp-user/temp-user.service.ts b/src/temp-user/temp-user.service.ts index 80760cf9ff093ae65069a182267049a22d536c6b..e43f10fd0691dc8394f16f85a27fe6d50e659178 100644 --- a/src/temp-user/temp-user.service.ts +++ b/src/temp-user/temp-user.service.ts @@ -75,7 +75,7 @@ export class TempUserService { * Send email in order to tell the user that an account is already fill with his structure info. * @param user User */ - public async sendUserMail(user: ITempUser, structureName: string, existingUser?: boolean): Promise<any> { + public async sendUserMail(user: ITempUser, structureName: string, existingUser?: boolean): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation( existingUser ? config.templates.userAddedToStructure.ejs : config.templates.tempUserRegistration.ejs diff --git a/src/users/controllers/jobs.controller.ts b/src/users/controllers/jobs.controller.ts index 13b6e1710eab2dd0304f77603a0ffdbb359eec21..323a00b34035f5a0e07a089693ba4c7ab1944fec 100644 --- a/src/users/controllers/jobs.controller.ts +++ b/src/users/controllers/jobs.controller.ts @@ -35,7 +35,7 @@ export class JobsController { * Return every jobs */ @Get() - public async findAll(): Promise<any> { + public async findAll() { this.logger.debug('findAll'); return this.jobsService.findAll(); } diff --git a/src/users/controllers/users.controller.ts b/src/users/controllers/users.controller.ts index b084cd6977a12bbcec7145ca01647d09378052fb..aadd984b1e1f8034831b3c139a7e9f0d33e42b6f 100644 --- a/src/users/controllers/users.controller.ts +++ b/src/users/controllers/users.controller.ts @@ -236,7 +236,13 @@ export class UsersController { @Get('join-validate/:token/:status') @ApiParam({ name: 'token', type: String, required: true }) @ApiParam({ name: 'status', type: String, required: true }) - public async joinValidation(@Param('token') token: string, @Param('status') status: string): Promise<any> { + public async joinValidation( + @Param('token') token: string, + @Param('status') status: string + ): Promise<{ + id: string; + name: string; + }> { const decoded: IPendingStructureToken = this.jwtService.decode(token) as IPendingStructureToken; const today = DateTime.local().setZone('utc', { keepLocalTime: true }); @@ -280,7 +286,7 @@ export class UsersController { @Get('join-cancel/:idStructure/:idUser') @ApiParam({ name: 'idStructure', type: String, required: true }) @ApiParam({ name: 'idUser', type: String, required: true }) - public async joinCancel(@Param('idStructure') idStructure: string, @Param('idUser') idUser: string): Promise<any> { + public async joinCancel(@Param('idStructure') idStructure: string, @Param('idUser') idUser: string): Promise<void> { // Get structure name const structure = await this.structureService.findOne(idStructure); if (!structure) { diff --git a/src/users/services/employer-search.service.spec.ts b/src/users/services/employer-search.service.spec.ts index b8d594eafbe4c016ab7671bb5284700890d0e44f..e41a421f04f0d512337527a498ee8042d41b61fd 100644 --- a/src/users/services/employer-search.service.spec.ts +++ b/src/users/services/employer-search.service.spec.ts @@ -46,7 +46,7 @@ describe('EmployerSearchService Search cases', () => { // Init test cases await service.dropIndex(); await service.createEmployerIndex(); - await Promise.all(employers.map((employer: any) => service.indexEmployer(employer))); + await Promise.all(employers.map((employer) => service.indexEmployer(employer))); // wait for the new structures to be indexed before search await service.refreshIndexStructure(); @@ -75,7 +75,7 @@ describe('EmployerSearchService Search cases', () => { }); it('should index structures', async () => { - const res = await Promise.all(employers.map((employer: any) => service.indexEmployer(employer))); + const res = await Promise.all(employers.map((employer) => service.indexEmployer(employer))); expect(res).toBeTruthy(); expect(res.length).toBe(5); diff --git a/src/users/services/employer-search.service.ts b/src/users/services/employer-search.service.ts index c41fc5d83e3a19f00f91f8d7e6f1ced6a62206d5..b48dbbfc745a248e348d6fd9f554b9df2c4b33ff 100644 --- a/src/users/services/employer-search.service.ts +++ b/src/users/services/employer-search.service.ts @@ -1,4 +1,9 @@ -import { IndicesRefreshResponse, UpdateResponse } from '@elastic/elasticsearch/lib/api/types'; +import { + IndicesCreateResponse, + IndicesRefreshResponse, + IndicesResponseBase, + UpdateResponse, +} from '@elastic/elasticsearch/lib/api/types'; import { Injectable, Logger } from '@nestjs/common'; import { ElasticsearchService } from '@nestjs/elasticsearch'; import { escapeElasticsearchQuery, es_settings_homemade_french } from '../../shared/utils'; @@ -24,7 +29,7 @@ export class EmployerSearchService { return employer; } - public async createEmployerIndex(): Promise<any> { + public async createEmployerIndex(): Promise<IndicesCreateResponse> { this.logger.debug('createEmployerIndex'); return this.elasticsearchService.indices.create({ index: this.index, @@ -46,7 +51,7 @@ export class EmployerSearchService { }); } - public async dropIndex(): Promise<any> { + public async dropIndex(): Promise<IndicesResponseBase> { this.logger.debug('dropIndex'); if ( await this.elasticsearchService.indices.exists({ @@ -97,7 +102,7 @@ export class EmployerSearchService { return sortedHits.map((item) => item._source); } - public async update(employer: EmployerDocument, id: string): Promise<UpdateResponse<any>> { + public async update(employer: EmployerDocument, id: string): Promise<UpdateResponse<unknown>> { this.logger.debug('update'); return this.elasticsearchService.update({ index: this.index, diff --git a/src/users/services/employer.service.ts b/src/users/services/employer.service.ts index d75bfdacebc9c2e68c4ecd7f4211ea34fc2d67b5..d60fc49a22ee5ae5929cb088cdc7852536f66480 100644 --- a/src/users/services/employer.service.ts +++ b/src/users/services/employer.service.ts @@ -64,8 +64,8 @@ export class EmployerService { public async sendAdminCreateNotification( employer: EmployerDocument, - templateLocation: any, - jsonConfigLocation: any + templateLocation: string, + jsonConfigLocation: string ): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(templateLocation); diff --git a/src/users/services/jobs.service.ts b/src/users/services/jobs.service.ts index 03ce9835f97071bd74f9a2162715757b2e3b54b8..e7d12fb4ab436e8ec4a6898ebcabf71a7ac84265 100644 --- a/src/users/services/jobs.service.ts +++ b/src/users/services/jobs.service.ts @@ -67,8 +67,8 @@ export class JobsService { public async sendAdminCreateNotification( job: JobDocument, - templateLocation: any, - jsonConfigLocation: any + templateLocation: string, + jsonConfigLocation: string ): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(templateLocation); diff --git a/src/users/services/userRegistry-search.service.spec.ts b/src/users/services/userRegistry-search.service.spec.ts index b6e995fd20cb6016545ea83dcdf4ef508104bdc3..0a6829c76649d9475292376fc7325ab671cdef97 100644 --- a/src/users/services/userRegistry-search.service.spec.ts +++ b/src/users/services/userRegistry-search.service.spec.ts @@ -19,7 +19,7 @@ describe('UserRegistrySearchService Search cases', () => { // Init test cases await service.dropIndex(); await service.createUserRegistryIndex(); - await Promise.all(multipleUsers.map((user: any) => service.indexUserRegistry(user))); + await Promise.all(multipleUsers.map((user) => service.indexUserRegistry(user))); // wait for the new structures to be indexed before search await service.refreshIndexUserRegistry(); @@ -47,7 +47,7 @@ describe('UserRegistrySearchService Search cases', () => { }); describe('Indexation methods', () => { it('should index User', async () => { - const res = await Promise.all(multipleUsers.map((user: any) => service.indexUserRegistry(user))); + const res = await Promise.all(multipleUsers.map((user) => service.indexUserRegistry(user))); expect(res).toBeTruthy(); expect(res.length).toBe(6); }); diff --git a/src/users/services/userRegistry-search.service.ts b/src/users/services/userRegistry-search.service.ts index efd2fdc14c97a6e526cc1bbf9012e28c31e9381f..13eaf6466b59c91f647c39cbc15dfb796ea5edcf 100644 --- a/src/users/services/userRegistry-search.service.ts +++ b/src/users/services/userRegistry-search.service.ts @@ -1,4 +1,9 @@ -import { IndicesCreateResponse, IndicesRefreshResponse, UpdateResponse } from '@elastic/elasticsearch/lib/api/types'; +import { + IndicesCreateResponse, + IndicesRefreshResponse, + IndicesResponseBase, + UpdateResponse, +} from '@elastic/elasticsearch/lib/api/types'; import { Injectable, Logger } from '@nestjs/common'; import { ElasticsearchService } from '@nestjs/elasticsearch'; import { escapeElasticsearchQuery, es_settings_homemade_french } from '../../shared/utils'; @@ -50,7 +55,7 @@ export class UserRegistrySearchService { }); } - public async dropIndex(): Promise<any> { + public async dropIndex(): Promise<IndicesResponseBase> { this.logger.debug('dropIndex'); const foundIndexes = await this.elasticsearchService.indices.exists({ index: this.index, @@ -100,7 +105,7 @@ export class UserRegistrySearchService { return sortedHits.map((item) => item._source); } - public async update(user: IUserRegistry): Promise<UpdateResponse<any>> { + public async update(user: IUserRegistry): Promise<UpdateResponse<unknown>> { this.logger.debug(`Updates user : ${user._id} `); return this.elasticsearchService.update<UserRegistrySearchBody>({ index: this.index, diff --git a/src/users/services/userRegistry.service.ts b/src/users/services/userRegistry.service.ts index d96f4ae6f7136ed1de6bfb7a7e565192a5b5b2a6..6b1ec1007ade98021cb66040f8bfbf88053c77c2 100644 --- a/src/users/services/userRegistry.service.ts +++ b/src/users/services/userRegistry.service.ts @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { IUser } from '../interfaces/user.interface'; +import { UserRegistrySearchBody } from '../interfaces/userRegistry-search-body.interface'; import { IUserRegistry, UserRegistryPaginatedResponse } from '../interfaces/userRegistry.interface'; import { Employer } from '../schemas/employer.schema'; import { Job } from '../schemas/job.schema'; @@ -131,7 +132,7 @@ export class UserRegistryService { } // SEARCH - public async searchByNameAndSurname(searchString: string): Promise<any[]> { + public async searchByNameAndSurname(searchString: string): Promise<UserRegistrySearchBody[]> { this.logger.debug('searchByNameAndSurname'); return this.userRegistrySearchService.search(searchString); } diff --git a/src/users/services/users.service.spec.ts b/src/users/services/users.service.spec.ts index 3ca3c313b64fc18c815e858c7397be264e871a28..5d86c8e787f45174a936a8e12f867f1172482801 100644 --- a/src/users/services/users.service.spec.ts +++ b/src/users/services/users.service.spec.ts @@ -14,7 +14,6 @@ import { ConfigurationModule } from '../../configuration/configuration.module'; import { MailerModule } from '../../mailer/mailer.module'; import { MailerService } from '../../mailer/mailer.service'; import { PersonalOfferDocument } from '../../personal-offers/schemas/personal-offer.schema'; -import { EmailChangeDto } from '../dto/change-email.dto'; import { CreateUserDto } from '../dto/create-user.dto'; import { DescriptionDto } from '../dto/description.dto'; import { UpdateDetailsDto } from '../dto/update-details.dto'; @@ -30,20 +29,20 @@ function hashPassword() { } type mockUserModelFunctionTypes = { - create: jest.Mock<any, any>; - deleteOne: jest.Mock<any, any>; - exec: jest.Mock<any, any>; - find: jest.Mock<any, any>; - findById: jest.Mock<any, any>; - findByIdAndUpdate: jest.Mock<any, any>; - findOne: jest.Mock<any, any>; - findPopulatedUserRegistryById: jest.Mock<any, any>; - limit: jest.Mock<any, any>; - populate: jest.Mock<any, any>; - select: jest.Mock<any, any>; - sort: jest.Mock<any, any>; - updateMany: jest.Mock<any, any>; - updateOne: jest.Mock<any, any>; + create: jest.Mock; + deleteOne: jest.Mock; + exec: jest.Mock; + find: jest.Mock; + findById: jest.Mock; + findByIdAndUpdate: jest.Mock; + findOne: jest.Mock; + findPopulatedUserRegistryById: jest.Mock; + limit: jest.Mock; + populate: jest.Mock; + select: jest.Mock; + sort: jest.Mock; + updateMany: jest.Mock; + updateOne: jest.Mock; }; const mockUserModel: mockUserModelFunctionTypes = { @@ -165,7 +164,7 @@ describe('UsersService', () => { }); it('should not create User, weak password', async () => { - jest.spyOn(service, 'findOne').mockImplementationOnce(async (): Promise<any> => null); + jest.spyOn(service, 'findOne').mockImplementationOnce(async () => null); jest.spyOn(service, 'isStrongPassword').mockImplementationOnce(() => false); try { await service.create(createUserDto); @@ -178,7 +177,7 @@ describe('UsersService', () => { } }); it('should create a User', async () => { - jest.spyOn(service, 'findOne').mockImplementationOnce(async (): Promise<any> => null); + jest.spyOn(service, 'findOne').mockImplementationOnce(async () => null); jest.spyOn(service, 'isStrongPassword').mockImplementationOnce(() => true); //TODO mock new userModal(createUserDto) return; @@ -262,12 +261,19 @@ describe('UsersService', () => { it('wrong password, should be unauthorized issue', async () => { const result: HttpException = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED); const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!!' }; //NOSONAR - jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<any> => result); - expect(await service.findByLogin(loginDto)).toBe(result); + jest.spyOn(service, 'findOne').mockResolvedValue(null); + try { + await service.findByLogin(loginDto); + expect(true).toBe(false); + } catch (error) { + expect(error.status).toBe(HttpStatus.UNAUTHORIZED); + expect(error.message).toBe('Invalid credentials'); + } }); }); describe('validateUser', () => { + // TODO implement correct tests it('should not validateUser', async () => { const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<HttpException> => result); @@ -276,174 +282,19 @@ describe('UsersService', () => { }); describe('changeUserPassword', () => { - it('should not change password', async () => { - const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); - jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result); - expect(await service.changeUserPassword('add3d', 'azertyU1', 'azertyU1!d')).toBe(result); - }); - it('should not change password', async () => { - const result = new HttpException('Invalid token', HttpStatus.UNPROCESSABLE_ENTITY); - jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result); - expect(await service.changeUserPassword('add3d', 'azertyU1!d', 'a')).toBe(result); - }); - it('should change password', async () => { - const result = new HttpException('Invalid token', HttpStatus.CREATED); - jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result); - expect(await service.changeUserPassword('add3d', 'azertyU1!d', 'azertyU1!d')).toBe(result); - }); + // TODO implement correct tests }); describe('changeUserEmail', () => { - it('should find and add token', async () => { - const result = { - validationToken: '', - emailVerified: true, - email: 'jacques.dupont@mii.com', - password: hashPassword(), - role: 0, - newEmail: 'test.dupont@mail.com', - resetPasswordToken: '', - structuresLink: [], - pendingStructuresLink: [], - structureOutdatedMailSent: [], - personalOffers: [], - name: 'Jacques', - surname: 'Dupont', - phone: '06 06 06 06 06', - createdAt: new Date('2022-05-25T09:48:28.824Z'), - changeEmailToken: - '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9', - employer: { - name: 'test', - validated: true, - }, - job: { - name: 'test', - validated: true, - hasPersonalOffer: false, - }, - unattachedSince: null, - }; - const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR - jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<User> => result); - expect(await service.changeUserEmail(emailDto)).toBe(result); - }); - it('user does not exist, should be unauthorized issue', async () => { - const result: HttpException = new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED); - const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR - jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result); - expect(await service.changeUserEmail(emailDto)).toBe(result); - }); - it('email already used, should be not acceptable issue', async () => { - const result: HttpException = new HttpException('Email already used', HttpStatus.NOT_ACCEPTABLE); - const emailDto: EmailChangeDto = { newEmail: 'jacques.dupont@mii.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR - jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result); - expect(await service.changeUserEmail(emailDto)).toBe(result); - }); - it('should change email', async () => { - const result = { - validationToken: '', - emailVerified: true, - email: 'test.dupont@mail.com', - password: hashPassword(), - role: 0, - newEmail: '', - resetPasswordToken: '', - changeEmailToken: '', - structuresLink: [], - pendingStructuresLink: [], - structureOutdatedMailSent: [], - personalOffers: [], - name: 'Jacques', - surname: 'Dupont', - phone: '06 06 06 06 06', - createdAt: new Date('2022-05-25T09:48:28.824Z'), - employer: { - name: 'test', - validated: true, - }, - job: { - name: 'test', - validated: true, - hasPersonalOffer: false, - }, - unattachedSince: null, - }; - const token = - '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9'; //NOSONAR - jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<User> => result); - expect(await service.verifyAndUpdateUserEmail(token)).toBe(result); - }); - it('should not change email', async () => { - const result: HttpException = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); - const token = '9bb3542bdc5ca8801aa72b8b6f56fc26950df30c8cd7e427a485f80181b9FAKETOKEN'; //NOSONAR - jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<any> => result); - expect(await service.verifyAndUpdateUserEmail(token)).toBe(result); - }); + // TODO implement correct tests }); describe('sendResetPasswordEmail', () => { - it('should not send email', async () => { - const result = new HttpException('Email sent if account exist', HttpStatus.OK); - jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result); - expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result); - }); - - it('should send email', async () => { - const result = new HttpException('Email sent if account exist', HttpStatus.OK); - jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result); - expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result); - }); + // TODO implement correct tests }); describe('validatePasswordResetToken', () => { - it('should not validate new password: token does`nt exist', async () => { - const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); - jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result); - expect( - await service.validatePasswordResetToken( - 'test@mii.com', - '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305' - ) - ).toBe(result); - }); - - it('should not validate new password: weak password', async () => { - const result = new HttpException( - 'Weak password, it must contain one lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer', - HttpStatus.UNPROCESSABLE_ENTITY - ); - jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result); - expect( - await service.validatePasswordResetToken( - 'test@mii.com', - '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6' - ) - ).toBe(result); - }); - - it('should validate new password', async () => { - const result = new HttpException('Password Reset', HttpStatus.OK); - jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result); - expect( - await service.validatePasswordResetToken( - 'test@mii.com', - '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6' - ) - ).toBe(result); - }); - - it('should return structureLink tab ', async () => { - const result = [53]; - jest.spyOn(service, 'updateStructureLinked').mockImplementation(async (): Promise<any> => result); - expect(await service.updateStructureLinked('test@mii.com', '6001a37716b08100062e4160')).toBe(result); - }); - - it('should return invalid User ', async () => { - const result = new HttpException('Invalid user', HttpStatus.NOT_FOUND); - jest.spyOn(service, 'updateStructureLinked').mockImplementation(async (): Promise<any> => result); - expect(await service.updateStructureLinked('test@mii.com', '6001a37716b08100062e4160')).toBe(result); - }); + // TODO implement correct tests }); it('should find All Unattacheduser', async () => { diff --git a/src/users/services/users.service.ts b/src/users/services/users.service.ts index 08a2ebfa23f2b3110b43d906582d935135b8ee9c..23138aa03dff355bbad6b4a05248536a4c9b44f5 100644 --- a/src/users/services/users.service.ts +++ b/src/users/services/users.service.ts @@ -41,7 +41,7 @@ export class UsersService { * Create a user account * @param createUserDto CreateUserDto */ - public async create(createUserDto: CreateUserDto): Promise<User | HttpStatus> { + public async create(createUserDto: CreateUserDto): Promise<IUser> { const userInDb = await this.findOne(createUserDto.email); if (userInDb) { throw new HttpException('User already exists', HttpStatus.BAD_REQUEST); @@ -52,7 +52,7 @@ export class UsersService { HttpStatus.UNPROCESSABLE_ENTITY ); } - let createUser = new this.userModel(createUserDto); + let createUser = new this.userModel(createUserDto) as IUser; createUser.surname = createUser.surname.toUpperCase(); createUser.structuresLink = []; if (createUserDto.structuresLink) { @@ -177,6 +177,7 @@ export class UsersService { * Use for login action * @param param LoginDto */ + // TODO update method name public async findByLogin({ email, password }: LoginDto): Promise<User> { const user = await this.findOne(email, true); @@ -199,7 +200,7 @@ export class UsersService { * a new account. * @param user User */ - private async verifyUserMail(user: IUser): Promise<any> { + private async verifyUserMail(user: IUser): Promise<IUser> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.verify.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.verify.json); @@ -223,7 +224,7 @@ export class UsersService { public async sendAdminApticStructureMail( structureName: string, duplicatedStructure: StructureDocument - ): Promise<any> { + ): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.apticStructureDuplication.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.apticStructureDuplication.json); @@ -243,7 +244,7 @@ export class UsersService { /** * Send to all admins mail for aptic duplicated data */ - public async sendAdminApticNewStructureMail(structure: StructureDocument): Promise<any> { + public async sendAdminApticNewStructureMail(structure: StructureDocument): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.newApticStructure.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.newApticStructure.json); @@ -263,9 +264,8 @@ export class UsersService { /** * Send approval email for user * a new account. - * @param user User */ - public async sendStructureClaimApproval(userEmail: string, structureName: string, status: boolean): Promise<any> { + public async sendStructureClaimApproval(userEmail: string, structureName: string, status: boolean): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.structureClaimValidation.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.structureClaimValidation.json); @@ -297,7 +297,7 @@ export class UsersService { } } - public async changeUserEmail(emailDto: EmailChangeDto): Promise<any> { + public async changeUserEmail(emailDto: EmailChangeDto): Promise<IUser> { const user = await this.findOne(emailDto.oldEmail); const alreadyUsed = await this.findOne(emailDto.newEmail); if (user) { @@ -321,7 +321,7 @@ export class UsersService { throw new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED); } - public async verifyAndUpdateUserEmail(token: string): Promise<any> { + public async verifyAndUpdateUserEmail(token: string): Promise<IUser> { const user = await this.userModel.findOne({ changeEmailToken: token }).exec(); if (user) { user.email = user.newEmail; @@ -334,7 +334,7 @@ export class UsersService { } } - public async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise<any> { + public async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise<void> { const user = await this.findById(userId, true); const arePasswordEqual = await this.comparePassword(oldPassword, user.password); if (!arePasswordEqual) { @@ -354,7 +354,7 @@ export class UsersService { * Send reset password email based on ejs template * @param email string */ - public async sendResetPasswordEmail(email: string): Promise<HttpException> { + public async sendResetPasswordEmail(email: string): Promise<void> { const user = await this.findOne(email); if (user) { const config = this.mailerService.config; @@ -381,7 +381,7 @@ export class UsersService { * @param password string * @param token string */ - public async validatePasswordResetToken(password: string, token: string): Promise<HttpException> { + public async validatePasswordResetToken(password: string, token: string): Promise<void> { const user = await this.userModel.findOne({ resetPasswordToken: token }).exec(); if (user) { if (!this.isStrongPassword(password)) { @@ -436,7 +436,7 @@ export class UsersService { return this.userModel.find({ employer: employerId._id }).select('-password -employer').exec(); } - public async populateJobswithUsers(jobs: JobDocument[]): Promise<any[]> { + public async populateJobswithUsers(jobs: JobDocument[]) { return Promise.all( jobs.map(async (job) => { return { @@ -450,7 +450,7 @@ export class UsersService { ); } - public async populateEmployerswithUsers(employers: EmployerDocument[]): Promise<any[]> { + public async populateEmployerswithUsers(employers: EmployerDocument[]) { return Promise.all( employers.map(async (employer) => { return { @@ -479,7 +479,7 @@ export class UsersService { * Send to all admins validation email for structures * new account. */ - private async sendAdminStructureValidationMail(userEmail: string, structure: StructureDocument): Promise<any> { + private async sendAdminStructureValidationMail(userEmail: string, structure: StructureDocument): Promise<void> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.adminStructureClaim.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.adminStructureClaim.json); @@ -802,7 +802,7 @@ export class UsersService { * @param employer * @param job */ - public async updateUserProfile(userId: Types.ObjectId, employer: EmployerDocument, job: JobDocument): Promise<any> { + public async updateUserProfile(userId: Types.ObjectId, employer: EmployerDocument, job: JobDocument): Promise<IUser> { this.logger.debug(`updateUserProfile | ${userId}`); const updated = await this.userModel.findByIdAndUpdate( { _id: userId }, @@ -820,7 +820,7 @@ export class UsersService { * @param job * @param userId */ - public async updateUserJob(userId: Types.ObjectId, job: JobDocument): Promise<any> { + public async updateUserJob(userId: Types.ObjectId, job: JobDocument): Promise<IUser> { this.logger.debug(`updateUserProfile - Job | ${userId}`); const updated = await this.userModel.findByIdAndUpdate({ _id: userId }, { $set: { job: job._id } }); if (updated) { @@ -835,7 +835,7 @@ export class UsersService { * @param employer * @param userId */ - public async updateUserEmployer(userId: Types.ObjectId, employer: EmployerDocument): Promise<any> { + public async updateUserEmployer(userId: Types.ObjectId, employer: EmployerDocument): Promise<IUser> { this.logger.debug(`updateUserProfile - Employer | ${userId}`); const updated = await this.userModel.findByIdAndUpdate({ _id: userId }, { $set: { employer: employer._id } }); if (updated) { diff --git a/test/mock/services/structures-for-search.mock.service.ts b/test/mock/services/structures-for-search.mock.service.ts index 6b0b5de417f25a08b92fc195e58ba9113287627b..cf92d736a3d7af1a4e6988f58517e0287d08382d 100644 --- a/test/mock/services/structures-for-search.mock.service.ts +++ b/test/mock/services/structures-for-search.mock.service.ts @@ -1,3 +1,5 @@ +import { StructureDocument } from '../../../src/structures/schemas/structure.schema'; + export class StructuresForSearchServiceMock { findAll() { return [ @@ -101,7 +103,7 @@ export class StructuresForSearchServiceMock { }, nbScanners: 1, description: "Notre rôle est de faciliter l'accès des personnes aux services nécessaires à la vie quotidienne", - }, - ]; + } as any, + ] as StructureDocument[]; } }