Ajout endpoint géolocalisation

This commit is contained in:
2024-12-07 15:11:47 +01:00
parent 1ae6b6634c
commit 86427bb41b
13 changed files with 240 additions and 21 deletions

View File

@ -3,9 +3,10 @@ import { PrismaService } from './prisma/prisma.service'
import { PrismaModule } from './prisma/prisma.module'
import { UsersModule } from './users/users.module'
import { AuthModule } from './auth/auth.module'
import { GeolocationsModule } from './geolocations/geolocations.module'
@Module({
imports: [PrismaModule, UsersModule, AuthModule],
imports: [PrismaModule, UsersModule, AuthModule, GeolocationsModule],
providers: [PrismaService],
})
export class AppModule {}

View File

@ -1,5 +1,9 @@
import { Injectable } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import { User } from '@prisma/client'
import { Request } from 'express'
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
export type AuthenticatedRequest = Request & { user: User }

View File

@ -0,0 +1,24 @@
import { ApiProperty } from "@nestjs/swagger"
export class CreateGeolocationDto {
@ApiProperty({description: "Longitude en degrés"})
longitude: number
@ApiProperty({description: "Latitude en degrés"})
latitude: number
@ApiProperty({description: "Vitesse en mètres par seconde"})
speed: number
@ApiProperty({description: "Précision en mètres de la position obtenue"})
accuracy: number
@ApiProperty({description: "Altitude en mètres"})
altitude: number
@ApiProperty({description: "Précision de l'altitude en mètres"})
altitudeAccuracy: number
@ApiProperty({description: "Date et heure de capture de la géolocalisation"})
timestamp: Date
}

View File

@ -0,0 +1,35 @@
import { ApiProperty } from "@nestjs/swagger"
import { Geolocation } from "@prisma/client"
export class GeolocationEntity implements Geolocation {
constructor(partial: Partial<GeolocationEntity>) {
Object.assign(this, partial)
}
@ApiProperty({description: "Identifiant unique"})
id: number
@ApiProperty({description: "Utilisateur⋅rice ayant émis la géolocalisation"})
userId: number
@ApiProperty({description: "Longitude en degrés"})
longitude: number
@ApiProperty({description: "Latitude en degrés"})
latitude: number
@ApiProperty({description: "Vitesse en mètres par seconde"})
speed: number
@ApiProperty({description: "Précision en mètres de la position obtenue"})
accuracy: number
@ApiProperty({description: "Altitude en mètres"})
altitude: number
@ApiProperty({description: "Précision de l'altitude en mètres"})
altitudeAccuracy: number
@ApiProperty({description: "Date et heure de capture de la géolocalisation"})
timestamp: Date
}

View File

@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing'
import { GeolocationsController } from './geolocations.controller'
import { GeolocationsService } from './geolocations.service'
describe('GeolocationsController', () => {
let controller: GeolocationsController
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [GeolocationsController],
providers: [GeolocationsService],
}).compile()
controller = module.get<GeolocationsController>(GeolocationsController)
})
it('should be defined', () => {
expect(controller).toBeDefined()
})
})

View File

@ -0,0 +1,63 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe, UseGuards, HttpCode, Req, NotFoundException } from '@nestjs/common'
import { GeolocationsService } from './geolocations.service'
import { CreateGeolocationDto } from './dto/create-geolocation.dto'
import { AuthenticatedRequest, JwtAuthGuard } from 'src/auth/jwt-auth.guard'
import { ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
import { GeolocationEntity } from './entities/geolocation.entity'
@Controller('geolocations')
export class GeolocationsController {
constructor(private readonly geolocationsService: GeolocationsService) {}
@Post()
@HttpCode(201)
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiCreatedResponse({ type: GeolocationEntity, description: "Objet créé avec succès" })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
@ApiNotFoundResponse({ description: "Object non trouvé" })
async create(@Req() request: AuthenticatedRequest, @Body() createGeolocationDto: CreateGeolocationDto): Promise<GeolocationEntity> {
const user = request.user
const geolocation = await this.geolocationsService.create(user, createGeolocationDto)
return new GeolocationEntity(geolocation)
}
@Get()
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ type: GeolocationEntity, isArray: true })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
@ApiNotFoundResponse({ description: "Objet non trouvé" })
async findAll(): Promise<GeolocationEntity[]> {
const geolocations = await this.geolocationsService.findAll()
return geolocations.map(geolocation => new GeolocationEntity(geolocation))
}
@Get(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ type: GeolocationEntity })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
@ApiNotFoundResponse({ description: "Objet non trouvé" })
async findOne(@Param('id', ParseIntPipe) id: number): Promise<GeolocationEntity> {
const geolocation = await this.geolocationsService.findOne(+id)
if (!geolocation)
throw new NotFoundException(`Géolocalisation inexistante avec l'identifiant ${id}`)
return new GeolocationEntity(geolocation)
}
@Delete(':id')
@HttpCode(204)
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiNoContentResponse({ description: "Objet supprimé avec succès" })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
@ApiNotFoundResponse({ description: "Objet non trouvé" })
async remove(@Param('id', ParseIntPipe) id: number): Promise<void> {
await this.geolocationsService.remove(+id)
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common'
import { GeolocationsService } from './geolocations.service'
import { GeolocationsController } from './geolocations.controller'
import { PrismaModule } from 'src/prisma/prisma.module'
@Module({
controllers: [GeolocationsController],
providers: [GeolocationsService],
imports: [PrismaModule],
})
export class GeolocationsModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing'
import { GeolocationsService } from './geolocations.service'
describe('GeolocationsService', () => {
let service: GeolocationsService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [GeolocationsService],
}).compile()
service = module.get<GeolocationsService>(GeolocationsService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})

View File

@ -0,0 +1,27 @@
import { Injectable } from '@nestjs/common'
import { CreateGeolocationDto } from './dto/create-geolocation.dto'
import { PrismaService } from 'src/prisma/prisma.service'
import { Geolocation, User } from '@prisma/client'
@Injectable()
export class GeolocationsService {
constructor(private prisma: PrismaService) { }
async create(authenticatedUser: User, createGeolocationDto: CreateGeolocationDto): Promise<Geolocation> {
const data = { ...createGeolocationDto, userId: authenticatedUser.id }
return await this.prisma.geolocation.create({ data: data })
}
async findAll(): Promise<Geolocation[]> {
return await this.prisma.geolocation.findMany()
}
async findOne(id: number): Promise<Geolocation> {
return await this.prisma.geolocation.findUnique({ where: { id } })
}
async remove(id: number): Promise<Geolocation> {
return await this.prisma.geolocation.delete({ where: { id } })
}
}

View File

@ -1,13 +1,10 @@
import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Post, Req, UseGuards } from '@nestjs/common'
import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Req, UseGuards } from '@nestjs/common'
import { UsersService } from './users.service'
import { ApiBadRequestResponse, ApiBearerAuth, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
import { UserEntity } from './entities/user.entity'
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'
import { User } from '@prisma/client'
import { AuthenticatedRequest, JwtAuthGuard } from 'src/auth/jwt-auth.guard'
import { UpdatePasswordDto } from './dto/user_password.dto'
export type AuthenticatedRequest = Request & { user: User }
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@ -45,8 +42,8 @@ export class UsersController {
@ApiBadRequestResponse({description: "Erreur dans la saisie du nouveau mot de passe."})
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
async updatePassword(@Req() request: AuthenticatedRequest, @Body() { password }: UpdatePasswordDto) {
async updatePassword(@Req() request: AuthenticatedRequest, @Body() body: UpdatePasswordDto) {
const user = request.user
await this.usersService.updatePassword(user, password)
await this.usersService.updatePassword(user, body)
}
}

View File

@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'
import { User } from '@prisma/client'
import { PrismaService } from 'src/prisma/prisma.service'
import * as bcrypt from 'bcrypt'
import { UpdatePasswordDto } from './dto/user_password.dto'
@Injectable()
export class UsersService {
@ -15,7 +16,7 @@ export class UsersService {
return await this.prisma.user.findUnique({ where: { id } })
}
async updatePassword(user: User, password: string) {
async updatePassword(user: User, { password }: UpdatePasswordDto) {
const hashedPassword = await bcrypt.hash(password, 10)
await this.prisma.user.update({
where: { id: user.id },