Skip to content

Commit

Permalink
feat: add signalement module (#493)
Browse files Browse the repository at this point in the history
* feat: add signalement module

* feat: add route to fetch signalement author

* review
  • Loading branch information
MaGOs92 authored Jan 2, 2025
1 parent e4a1c49 commit 2e18efb
Show file tree
Hide file tree
Showing 45 changed files with 1,699 additions and 21 deletions.
5 changes: 4 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ API_URL=http://localhost:5000
API_DEPOT_URL=https://plateforme-bal.adresse.data.gouv.fr/api-depot
API_DEPOT_CLIENT_ID=
API_DEPOT_CLIENT_SECRET=
BAN_API_URL=https://plateforme.adresse.data.gouv.fr
BAN_API_URL=https://plateforme.adresse.data.gouv.fr

API_SIGNALEMENT_URL=https://api-signalement-prod.osc-secnum-fr1.scalingo.io/
API_SIGNALEMENT_CLIENT_SECRET=
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,26 @@ $ yarn lint
Cette application utilise des variables d'environnement pour sa configuration.
Elles peuvent être définies classiquement ou en créant un fichier `.env` sur la base du modèle `.env.sample`.

| Nom de la variable | Description |
| ------------------------- | --------------------------------------------------------------------------- |
| `POSTGRES_URL` | Url de connection a la db postgres |
| `PORT` | Port à utiliser pour l'API |
| `API_URL` | URL de base de l’API |
| `API_DEPOT_URL` | URL de l'api-depot |
| `API_DEPOT_CLIENT_ID` | Id du client de l'api-depot |
| `API_DEPOT_CLIENT_SECRET` | Token du client de l'api-depot |
| `EDITOR_URL_PATTERN` | Pattern permettant de construire l'URL vers l'édition d'une BAL |
| `BAN_API_URL` | URL de ban-plateform |
| --- | --- |
| `SMTP_HOST` | Nom d'hôte du serveur SMTP |
| `SMTP_PORT` | Port du serveur SMTP |
| `SMTP_USER` | Nom d'utilisateur pour se connecter au serveur SMTP |
| `SMTP_PASS` | Mot de passe pour se connecter au serveur SMTP |
| `SMTP_SECURE` | Indique si le serveur SMTP nécessite une connexion sécurisée (`YES`) |
| `SMTP_FROM` | Adresse à utiliser en tant qu'expéditeur des emails |
| `SMTP_BCC` | Adresse(s) en copie cachée à utiliser pour tous les envois de notifications |
| Nom de la variable | Description |
| ------------------------------- | --------------------------------------------------------------------------- |
| `POSTGRES_URL` | Url de connection a la db postgres |
| `PORT` | Port à utiliser pour l'API |
| `API_URL` | URL de base de l’API |
| `API_DEPOT_URL` | URL de l'api-depot |
| `API_DEPOT_CLIENT_ID` | Id du client de l'api-depot |
| `API_DEPOT_CLIENT_SECRET` | Token du client de l'api-depot |
| `EDITOR_URL_PATTERN` | Pattern permettant de construire l'URL vers l'édition d'une BAL |
| `BAN_API_URL` | URL de ban-plateform |
| `API_SIGNALEMENT_URL` | URL de l'API Signalement |
| `API_SIGNALEMENT_CLIENT_SECRET` | Secret du client de l'API Signalement |
| --- | --- |
| `SMTP_HOST` | Nom d'hôte du serveur SMTP |
| `SMTP_PORT` | Port du serveur SMTP |
| `SMTP_USER` | Nom d'utilisateur pour se connecter au serveur SMTP |
| `SMTP_PASS` | Mot de passe pour se connecter au serveur SMTP |
| `SMTP_SECURE` | Indique si le serveur SMTP nécessite une connexion sécurisée (`YES`) |
| `SMTP_FROM` | Adresse à utiliser en tant qu'expéditeur des emails |
| `SMTP_BCC` | Adresse(s) en copie cachée à utiliser pour tous les envois de notifications |

Toutes ces variables ont des valeurs par défaut que vous trouverez dans le fichier `.env.sample`.

Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { StatsModule } from './modules/stats/stats.module';
import { MailerModule } from '@nestjs-modules/mailer';
import { MailerParams } from '@/shared/params/mailer.params';
import { AdminModule } from './modules/admin/admin.module';
import { SignalementModule } from './modules/signalement/signalement.module';

@Module({
imports: [
Expand All @@ -44,6 +45,7 @@ import { AdminModule } from './modules/admin/admin.module';
ToponymeModule,
StatsModule,
AdminModule,
SignalementModule,
],
controllers: [],
providers: [],
Expand Down
18 changes: 18 additions & 0 deletions apps/api/src/modules/signalement/dto/update-signalement-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Signalement } from '@/shared/openapi-signalement';
import { ApiProperty } from '@nestjs/swagger';
import { ArrayMinSize, IsEnum } from 'class-validator';

export class UpdateSignalementDTO {
@ApiProperty({
type: String,
required: true,
nullable: false,
isArray: true,
})
@ArrayMinSize(1)
ids: string[];

@ApiProperty({ required: true, nullable: false, enum: Signalement.status })
@IsEnum(Signalement.status)
status: Signalement.status;
}
32 changes: 32 additions & 0 deletions apps/api/src/modules/signalement/openAPI-signalement.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

import {
SignalementsService,
OpenAPI as OpenAPISignalement,
UpdateSignalementDTO,
} from '@/shared/openapi-signalement';

@Injectable()
export class OpenAPISignalementService {
constructor(private configService: ConfigService) {
OpenAPISignalement.BASE = this.configService.get('API_SIGNALEMENT_URL');
OpenAPISignalement.TOKEN = this.configService.get(
'API_SIGNALEMENT_CLIENT_SECRET',
);
}

getSignalementById(signalementId: string) {
return SignalementsService.getSignalementById(signalementId);
}

async updateSignalement(
signalementId: string,
updateSignalementDTO: UpdateSignalementDTO,
) {
return SignalementsService.updateSignalement(
signalementId,
updateSignalementDTO,
);
}
}
84 changes: 84 additions & 0 deletions apps/api/src/modules/signalement/signalement.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
Body,
Controller,
Get,
HttpStatus,
Inject,
Param,
Put,
Req,
Res,
UseGuards,
forwardRef,
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiBody,
ApiOperation,
ApiParam,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { Response } from 'express';

import { AdminGuard } from '@/lib/guards/admin.guard';
import { CustomRequest } from '@/lib/types/request.type';
import { SignalementService } from './signalement.service';
import { UpdateSignalementDTO } from './dto/update-signalement-dto';

@ApiTags('signalements')
@Controller('signalements')
export class SignalementController {
constructor(
@Inject(forwardRef(() => SignalementService))
private signalementService: SignalementService,
) {}

@Put(':baseLocaleId')
@ApiOperation({
summary: 'Update signalements',
operationId: 'updateSignalements',
})
@ApiParam({ name: 'baseLocaleId', required: true, type: String })
@ApiBody({ type: UpdateSignalementDTO, required: true })
@ApiResponse({
status: HttpStatus.OK,
type: Boolean,
})
@ApiBearerAuth('admin-token')
@UseGuards(AdminGuard)
async update(
@Req() req: CustomRequest,
@Body() updateSignalementDTO: UpdateSignalementDTO,
@Res() res: Response,
) {
await this.signalementService.updateMany(
req.baseLocale,
updateSignalementDTO,
);
res.status(HttpStatus.OK).json(true);
}

@Get('/:baseLocaleId/:idSignalement/author')
@ApiOperation({
summary: 'Get author by signalement id',
operationId: 'getAuthor',
})
@ApiParam({ name: 'baseLocaleId', required: true, type: String })
@ApiParam({ name: 'idSignalement', required: true, type: String })
@ApiBearerAuth('admin-token')
@UseGuards(AdminGuard)
@ApiResponse({
status: HttpStatus.OK,
})
async getAuthor(
@Req() req: Request,
@Res() res: Response,
@Param('idSignalement') idSignalement: string,
) {
const signalement =
await this.signalementService.findOneOrFail(idSignalement);

res.status(HttpStatus.OK).json(signalement.author);
}
}
20 changes: 20 additions & 0 deletions apps/api/src/modules/signalement/signalement.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Module, MiddlewareConsumer, forwardRef } from '@nestjs/common';

import { BaseLocaleMiddleware } from '@/modules/base_locale/base_locale.middleware';
import { ConfigModule } from '@nestjs/config';
import { BaseLocaleModule } from '../base_locale/base_locale.module';
import { SignalementService } from './signalement.service';
import { SignalementController } from './signalement.controller';
import { OpenAPISignalementService } from './openAPI-signalement.service';

@Module({
imports: [ConfigModule, forwardRef(() => BaseLocaleModule)],
providers: [SignalementService, OpenAPISignalementService],
controllers: [SignalementController],
exports: [SignalementService],
})
export class SignalementModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(BaseLocaleMiddleware).forRoutes(SignalementController);
}
}
57 changes: 57 additions & 0 deletions apps/api/src/modules/signalement/signalement.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
BaseLocale,
StatusBaseLocalEnum,
} from '@/shared/entities/base_locale.entity';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { UpdateSignalementDTO } from './dto/update-signalement-dto';
import { OpenAPISignalementService } from './openAPI-signalement.service';

@Injectable()
export class SignalementService {
constructor(private openAPISignalementService: OpenAPISignalementService) {}

async findOneOrFail(signalementId: string) {
const fetchedSignalement =
await this.openAPISignalementService.getSignalementById(signalementId);

if (!fetchedSignalement) {
throw new HttpException(
`Signalement ${signalementId} not found`,
HttpStatus.NOT_FOUND,
);
}

return fetchedSignalement;
}

async updateMany(
baseLocale: BaseLocale,
updateSignalementDTO: UpdateSignalementDTO,
) {
const { ids, status } = updateSignalementDTO;

if (baseLocale.status !== StatusBaseLocalEnum.PUBLISHED) {
throw new HttpException(
'BaseLocale is not published',
HttpStatus.PRECONDITION_FAILED,
);
}

for (const signalementId of ids) {
const fetchedSignalement = await this.findOneOrFail(signalementId);

if (baseLocale.commune !== fetchedSignalement.codeCommune) {
throw new HttpException(
`Communes do not match for signalement ${signalementId}`,
HttpStatus.PRECONDITION_FAILED,
);
}

await this.openAPISignalementService.updateSignalement(signalementId, {
status,
});
}

return true;
}
}
Loading

0 comments on commit 2e18efb

Please sign in to comment.