Ajout endpoint géolocalisation

This commit is contained in:
Emmy D'Anello 2024-12-07 15:11:47 +01:00
parent 1ae6b6634c
commit 86427bb41b
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
13 changed files with 240 additions and 21 deletions

View File

@ -0,0 +1,14 @@
/*
Warnings:
- Added the required column `accuracy` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
- Added the required column `altitude` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
- Added the required column `altitudeAccuracy` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
- Added the required column `speed` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Geolocation" ADD COLUMN "accuracy" DOUBLE PRECISION NOT NULL,
ADD COLUMN "altitude" DOUBLE PRECISION NOT NULL,
ADD COLUMN "altitudeAccuracy" DOUBLE PRECISION NOT NULL,
ADD COLUMN "speed" DOUBLE PRECISION NOT NULL;

View File

@ -21,11 +21,15 @@ model User {
model Geolocation {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int
longitude Float
latitude Float
speed Float
accuracy Float
altitude Float
altitudeAccuracy Float
timestamp DateTime
user User @relation(fields: [userId], references: [id])
}
model Challenge {
@ -38,17 +42,18 @@ model Challenge {
model ChallengeAction {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int
challenge Challenge @relation(fields: [challengeId], references: [id])
challengeId Int @unique
active Boolean @default(false)
success Boolean @default(false)
challenge Challenge @relation(fields: [challengeId], references: [id])
user User @relation(fields: [userId], references: [id])
moneyUpdate MoneyUpdate?
}
model TrainTrip {
id String @id
user User @relation(fields: [userId], references: [id])
userId Int
distance Float
from String
@ -58,20 +63,19 @@ model TrainTrip {
infoJson Json
geometry String
moneyUpdate MoneyUpdate?
user User @relation(fields: [userId], references: [id])
}
model MoneyUpdate {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int
before Int
after Int
reason MoneyUpdateType
actionId Int? @unique
tripId String? @unique
action ChallengeAction? @relation(fields: [actionId], references: [id])
actionId Int? @unique
trip TrainTrip? @relation(fields: [tripId], references: [id])
user User @relation(fields: [userId], references: [id])
tripId String? @unique
}
enum MoneyUpdateType {

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 },