Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions apps/api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 3001);
const app = await NestFactory.create(AppModule);

// Activer CORS
app.enableCors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
credentials: true,
});

await app.listen(process.env.PORT || 3001);
}
bootstrap();
2 changes: 0 additions & 2 deletions apps/api/src/modules/exercise/exercise.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ export class ExerciseController implements NestControllerInterface<typeof c> {

@TsRest(c.getExercise)
async getExercise(@TsRestRequest() { params }: RequestShapes['getExercise']) {
// Dans le contrat, pathParams = { id: z.string() }
// => on cast en number (ou on utilise z.coerce.number() dans le contrat)
try {
const exercise = await this.exerciseService.getExercise(params.id);

Expand Down
161 changes: 131 additions & 30 deletions apps/api/src/modules/exerciseCategory/exerciseCategory.controller.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,152 @@
import { exerciseCategoryContract } from '@dropit/contract';
import {
Body,
BadRequestException,
Controller,
Delete,
Get,
Param,
Post,
Put,
NotFoundException,
} from '@nestjs/common';
import { ExerciseCategory } from '../../entities/exerciseCategory.entity';
import {
NestControllerInterface,
NestRequestShapes,
TsRest,
TsRestRequest,
nestControllerContract,
} from '@ts-rest/nest';
import { ExerciseCategoryService } from './exerciseCategory.service';

@Controller('exercise-category')
export class ExerciseCategoryController {
const c = nestControllerContract(exerciseCategoryContract);
type RequestShapes = NestRequestShapes<typeof c>;

@Controller()
export class ExerciseCategoryController
implements NestControllerInterface<typeof c>
{
constructor(
private readonly exerciseCategoryService: ExerciseCategoryService
) {}

@Get()
async getExerciseCategories() {
return this.exerciseCategoryService.getExerciseCategories();
@TsRest(c.getExerciseCategories)
async getExerciseCategories(
@TsRestRequest() request: RequestShapes['getExerciseCategories']
) {
try {
const exerciseCategories =
await this.exerciseCategoryService.getExerciseCategories();
return {
status: 200 as const,
body: exerciseCategories,
};
} catch (error) {
if (error instanceof NotFoundException) {
return {
status: 404 as const,
body: {
message: error.message,
},
};
}
throw error;
}
}

@Get(':id')
async getExerciseCategory(@Param('id') id: string) {
return this.exerciseCategoryService.getExerciseCategory(id);
@TsRest(c.getExerciseCategory)
async getExerciseCategory(
@TsRestRequest()
{ params }: RequestShapes['getExerciseCategory']
) {
try {
const exerciseCategory =
await this.exerciseCategoryService.getExerciseCategory(params.id);
return {
status: 200 as const,
body: exerciseCategory,
};
} catch (error) {
if (error instanceof NotFoundException) {
return {
status: 404 as const,
body: {
message: error.message,
},
};
}
throw error;
}
}

@Post()
async createExerciseCategory(@Body() exerciseCategory: ExerciseCategory) {
return this.exerciseCategoryService.createExerciseCategory(
exerciseCategory
);
@TsRest(c.createExerciseCategory)
async createExerciseCategory(
@TsRestRequest() { body }: RequestShapes['createExerciseCategory']
) {
try {
const newExerciseCategory =
await this.exerciseCategoryService.createExerciseCategory(body);
return {
status: 201 as const,
body: newExerciseCategory,
};
} catch (error) {
if (error instanceof BadRequestException) {
return {
status: 400 as const,
body: {
message: error.message,
},
};
}
throw error;
}
}

@Put(':id')
@TsRest(c.updateExerciseCategory)
async updateExerciseCategory(
@Param('id') id: string,
@Body() exerciseCategory: ExerciseCategory
@TsRestRequest() { params, body }: RequestShapes['updateExerciseCategory']
) {
return this.exerciseCategoryService.updateExerciseCategory(
id,
exerciseCategory
);
try {
const updatedExerciseCategory =
await this.exerciseCategoryService.updateExerciseCategory(
params.id,
body
);
return {
status: 200 as const,
body: updatedExerciseCategory,
};
} catch (error) {
if (error instanceof NotFoundException) {
return {
status: 404 as const,
body: {
message: error.message,
},
};
}
throw error;
}
}

@Delete(':id')
async deleteExerciseCategory(@Param('id') id: string) {
return this.exerciseCategoryService.deleteExerciseCategory(id);
@TsRest(c.deleteExerciseCategory)
async deleteExerciseCategory(
@TsRestRequest() { params }: RequestShapes['deleteExerciseCategory']
) {
try {
await this.exerciseCategoryService.deleteExerciseCategory(params.id);

return {
status: 200 as const,
body: {
message: 'Exercise category deleted successfully',
},
};
} catch (error) {
if (error instanceof NotFoundException) {
return {
status: 404 as const,
body: {
message: error.message,
},
};
}
throw error;
}
}
}
3 changes: 0 additions & 3 deletions apps/api/src/modules/exerciseCategory/exerciseCategory.dto.ts

This file was deleted.

92 changes: 77 additions & 15 deletions apps/api/src/modules/exerciseCategory/exerciseCategory.service.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,73 @@
import {
CreateExerciseCategory,
ExerciseCategoryDto,
UpdateExerciseCategory,
} from '@dropit/schemas';
import { EntityManager, wrap } from '@mikro-orm/postgresql';
import { Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { ExerciseCategory } from '../../entities/exerciseCategory.entity';
import { ExerciseCategoryDto } from './exerciseCategory.dto';

@Injectable()
export class ExerciseCategoryService {
constructor(private readonly em: EntityManager) {}

async getExerciseCategories() {
return this.em.find(ExerciseCategory, {});
async getExerciseCategories(): Promise<ExerciseCategoryDto[]> {
const exerciseCategories = await this.em.find(ExerciseCategory, {});

return exerciseCategories.map((exerciseCategory) => ({
id: exerciseCategory.id,
name: exerciseCategory.name,
}));
}

async getExerciseCategory(id: string) {
return this.em.findOne(ExerciseCategory, { id });
async getExerciseCategory(id: string): Promise<ExerciseCategoryDto> {
const exerciseCategory = await this.em.findOne(ExerciseCategory, { id });

if (!exerciseCategory) {
throw new NotFoundException(`Exercise category with ID ${id} not found`);
}

return {
id: exerciseCategory.id,
name: exerciseCategory.name,
};
}

async createExerciseCategory(exerciseCategory: ExerciseCategoryDto) {
async createExerciseCategory(
newExerciseCategory: CreateExerciseCategory
): Promise<ExerciseCategoryDto> {
if (!newExerciseCategory.name) {
throw new BadRequestException('Exercise category name is required');
}

const exerciseCategoryToCreate = new ExerciseCategory();
exerciseCategoryToCreate.name = exerciseCategory.name;
exerciseCategoryToCreate.name = newExerciseCategory.name;
await this.em.persistAndFlush(exerciseCategoryToCreate);
return exerciseCategoryToCreate;

const exerciseCategoryCreated = await this.em.findOne(ExerciseCategory, {
id: exerciseCategoryToCreate.id,
});

if (!exerciseCategoryCreated) {
throw new NotFoundException(
`Exercise category with ID ${exerciseCategoryToCreate.id} not found`
);
}

return {
id: exerciseCategoryCreated.id,
name: exerciseCategoryCreated.name,
};
}

async updateExerciseCategory(
id: string,
exerciseCategory: ExerciseCategoryDto
) {
exerciseCategory: UpdateExerciseCategory
): Promise<ExerciseCategoryDto> {
const exerciseCategoryToUpdate = await this.em.findOne(ExerciseCategory, {
id,
});
Expand All @@ -34,21 +76,41 @@ export class ExerciseCategoryService {
throw new Error('Exercise category not found');
}

wrap(exerciseCategoryToUpdate).assign(exerciseCategory);
wrap(exerciseCategoryToUpdate).assign(exerciseCategory, {
mergeObjectProperties: true,
});

await this.em.persistAndFlush(exerciseCategoryToUpdate);
return exerciseCategoryToUpdate;

const exerciseCategoryUpdated = await this.em.findOne(ExerciseCategory, {
id: exerciseCategoryToUpdate.id,
});

if (!exerciseCategoryUpdated) {
throw new NotFoundException(
`Exercise category with ID ${exerciseCategoryToUpdate.id} not found`
);
}

return {
id: exerciseCategoryUpdated.id,
name: exerciseCategoryUpdated.name,
};
}

async deleteExerciseCategory(id: string) {
async deleteExerciseCategory(id: string): Promise<{ message: string }> {
const exerciseCategoryToDelete = await this.em.findOne(ExerciseCategory, {
id,
});

if (!exerciseCategoryToDelete) {
throw new Error('Exercise category not found');
throw new NotFoundException(`Exercise category with ID ${id} not found`);
}

await this.em.removeAndFlush(exerciseCategoryToDelete);

return {
message: 'Exercise category deleted successfully',
};
}
}
13 changes: 8 additions & 5 deletions apps/api/test/exercise.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { CreateExercise, ExerciseDto } from '@dropit/schemas';
import {
CreateExercise,
ExerciseCategoryDto,
ExerciseDto,
} from '@dropit/schemas';
import { MikroORM } from '@mikro-orm/core';
import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import request from 'supertest';
import { AppModule } from '../src/app.module';
import { ExerciseCategory } from '../src/entities/exerciseCategory.entity';
import { ExerciseService } from '../src/modules/exercise/exercise.service';
import { ExerciseCategoryService } from '../src/modules/exerciseCategory/exerciseCategory.service';

describe('ExerciseController (e2e)', () => {
let app: INestApplication;
let orm: MikroORM;
let exerciseCategory: ExerciseCategory;
let exerciseCategory: ExerciseCategoryDto;

// Déclaration des fonctions utilitaires
let createExerciseCategory: (name: string) => Promise<ExerciseCategory>;
let createExerciseCategory: (name: string) => Promise<ExerciseCategoryDto>;
let createExercise: (exercise: CreateExercise) => Promise<ExerciseDto>;

beforeAll(async () => {
Expand All @@ -37,7 +40,7 @@ describe('ExerciseController (e2e)', () => {

createExerciseCategory = async (
name: string
): Promise<ExerciseCategory> => {
): Promise<ExerciseCategoryDto> => {
return await exerciseCategoryService.createExerciseCategory({ name });
};

Expand Down
Loading
Loading