From 8424ea772d6abd6da412ee66b12cd6591c564c0a Mon Sep 17 00:00:00 2001 From: justnyx Date: Wed, 13 Nov 2024 16:59:44 +0100 Subject: [PATCH 1/2] added reservation crud --- apps/backend/src/dto/pagination.dto.ts | 6 ++ .../dto/create-reservation.dto.ts | 5 + .../dto/simple-reservation.dto.ts | 5 + .../dto/update-reservation.dto.ts | 5 + .../entities/reservation.entity.ts | 29 +++++ .../reservations/reservations.controller.ts | 35 ++++++ .../src/reservations/reservations.module.ts | 10 ++ .../src/reservations/reservations.service.ts | 102 ++++++++++++++++++ 8 files changed, 197 insertions(+) create mode 100644 apps/backend/src/dto/pagination.dto.ts create mode 100644 apps/backend/src/reservations/dto/create-reservation.dto.ts create mode 100644 apps/backend/src/reservations/dto/simple-reservation.dto.ts create mode 100644 apps/backend/src/reservations/dto/update-reservation.dto.ts create mode 100644 apps/backend/src/reservations/entities/reservation.entity.ts create mode 100644 apps/backend/src/reservations/reservations.controller.ts create mode 100644 apps/backend/src/reservations/reservations.module.ts create mode 100644 apps/backend/src/reservations/reservations.service.ts diff --git a/apps/backend/src/dto/pagination.dto.ts b/apps/backend/src/dto/pagination.dto.ts new file mode 100644 index 0000000..9d0b4d0 --- /dev/null +++ b/apps/backend/src/dto/pagination.dto.ts @@ -0,0 +1,6 @@ +export type PaginationDto = { + data: T[]; + count: number; + page: number; + limit: number; +}; diff --git a/apps/backend/src/reservations/dto/create-reservation.dto.ts b/apps/backend/src/reservations/dto/create-reservation.dto.ts new file mode 100644 index 0000000..6e3526b --- /dev/null +++ b/apps/backend/src/reservations/dto/create-reservation.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { Reservation } from '../entities/reservation.entity'; + +export class CreateReservationDto extends OmitType(Reservation, ['id', 'userId']) {} diff --git a/apps/backend/src/reservations/dto/simple-reservation.dto.ts b/apps/backend/src/reservations/dto/simple-reservation.dto.ts new file mode 100644 index 0000000..7ddcf96 --- /dev/null +++ b/apps/backend/src/reservations/dto/simple-reservation.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { Reservation } from '../entities/reservation.entity'; + +export class SimpleReservationDto extends OmitType(Reservation, ['id', 'userId']) {} diff --git a/apps/backend/src/reservations/dto/update-reservation.dto.ts b/apps/backend/src/reservations/dto/update-reservation.dto.ts new file mode 100644 index 0000000..f16bf66 --- /dev/null +++ b/apps/backend/src/reservations/dto/update-reservation.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from '@nestjs/mapped-types'; + +import { CreateReservationDto } from './create-reservation.dto'; + +export class UpdateReservationDto extends PartialType(CreateReservationDto) {} diff --git a/apps/backend/src/reservations/entities/reservation.entity.ts b/apps/backend/src/reservations/entities/reservation.entity.ts new file mode 100644 index 0000000..42ff503 --- /dev/null +++ b/apps/backend/src/reservations/entities/reservation.entity.ts @@ -0,0 +1,29 @@ +import { ReservationStatus } from '@prisma/client'; +import { IsDate, IsEnum, IsNotEmpty, IsNumber } from 'class-validator'; + +export class Reservation { + @IsNotEmpty() + @IsNumber() + id: number; + + @IsNumber() + userId: number; + + @IsNumber() + bandId: number; + + @IsNotEmpty() + @IsDate() + startTime: Date; + + @IsNotEmpty() + @IsDate() + endTime: Date; + + @IsNumber() + gateKeeperId: number; + + @IsNotEmpty() + @IsEnum(ReservationStatus) + status: ReservationStatus; +} diff --git a/apps/backend/src/reservations/reservations.controller.ts b/apps/backend/src/reservations/reservations.controller.ts new file mode 100644 index 0000000..2d663a3 --- /dev/null +++ b/apps/backend/src/reservations/reservations.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; + +import { CreateReservationDto } from './dto/create-reservation.dto'; +import { UpdateReservationDto } from './dto/update-reservation.dto'; +import { ReservationsService } from './reservations.service'; + +@Controller('reservations') +export class ReservationsController { + constructor(private readonly reservationsService: ReservationsService) {} + + @Post() + create(@Body() createReservationDto: CreateReservationDto) { + return this.reservationsService.create(createReservationDto); + } + + @Get() + async findAll(@Query('page', ParseIntPipe) page: number, @Query('page_size', ParseIntPipe) pageSize: number) { + return this.reservationsService.findAll(page, pageSize); + } + + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + return this.reservationsService.findOne(id); + } + + @Patch(':id') + update(@Param('id', ParseIntPipe) id: number, @Body() updateReservationDto: UpdateReservationDto) { + return this.reservationsService.update(id, updateReservationDto); + } + + @Delete(':id') + remove(@Param('id', ParseIntPipe) id: number) { + return this.reservationsService.remove(id); + } +} diff --git a/apps/backend/src/reservations/reservations.module.ts b/apps/backend/src/reservations/reservations.module.ts new file mode 100644 index 0000000..49f1544 --- /dev/null +++ b/apps/backend/src/reservations/reservations.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { ReservationsController } from './reservations.controller'; +import { ReservationsService } from './reservations.service'; + +@Module({ + controllers: [ReservationsController], + providers: [ReservationsService], +}) +export class ReservationsModule {} diff --git a/apps/backend/src/reservations/reservations.service.ts b/apps/backend/src/reservations/reservations.service.ts new file mode 100644 index 0000000..00f5f9b --- /dev/null +++ b/apps/backend/src/reservations/reservations.service.ts @@ -0,0 +1,102 @@ +import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; +import { PrismaService } from 'nestjs-prisma'; + +import { PaginationDto } from '../dto/pagination.dto'; +import { CreateReservationDto } from './dto/create-reservation.dto'; +import { UpdateReservationDto } from './dto/update-reservation.dto'; +import { Reservation } from './entities/reservation.entity'; + +@Injectable() +export class ReservationsService { + constructor(private readonly prisma: PrismaService) {} + + create(createReservationDto: CreateReservationDto) { + return this.prisma.reservation.create({ + data: { + ...createReservationDto, + }, + }); + } + + findAll(page?: number, pageSize?: number): Promise> { + const hasPagination = page !== -1 && pageSize !== -1; + const reservations = this.prisma.reservation.findMany({ + skip: hasPagination ? (page - 1) * pageSize : undefined, + take: hasPagination ? pageSize : undefined, + orderBy: { + startTime: 'asc', + }, + }); + + const count = this.prisma.reservation.count(); + + return Promise.all([reservations, count]) + .then(([reservations, count]) => { + const limit = hasPagination ? Math.floor(count / pageSize) : 0; + return { + data: reservations, + count, + page, + limit, + }; + }) + .catch(() => { + throw new InternalServerErrorException('An error occurred.'); + }); + } + + async findOne(id: number) { + try { + return await this.prisma.reservation.findUniqueOrThrow({ + where: { + id, + }, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + if (e.code === 'P2025') { + throw new NotFoundException(`This reservation doesn't exist.`); + } + throw new InternalServerErrorException('An error occurred.'); + } + } + } + + async update(id: number, updateReservationDto: UpdateReservationDto) { + try { + return await this.prisma.reservation.update({ + where: { + id, + }, + data: { + ...updateReservationDto, + }, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + if (e.code === 'P2025') { + throw new NotFoundException(`This reservation doesn't exist.`); + } + throw new InternalServerErrorException('An error occurred.'); + } + } + } + + remove(id: number) { + try { + return this.prisma.reservation.delete({ + where: { + id, + }, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + if (e.code === 'P2025') { + throw new NotFoundException(`This reservation doesn't exist.`); + } + throw new InternalServerErrorException('An error occurred.'); + } + } + } +} From 36c50add2709daee9f22e4f268226f1bf6d7b4e9 Mon Sep 17 00:00:00 2001 From: justnyx Date: Wed, 13 Nov 2024 17:12:56 +0100 Subject: [PATCH 2/2] updated package.json and yarn.lock --- apps/backend/package.json | 2 + package.json | 3 ++ yarn.lock | 81 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 61e1ee4..ec97bf4 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -19,6 +19,8 @@ "@nestjs/platform-express": "^10.3.8", "@nestjs/swagger": "^7.3.1", "@prisma/client": "^5.13.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "nestjs-prisma": "^0.23.0", "reflect-metadata": "^0.2.2", "rimraf": "^5.0.5", diff --git a/package.json b/package.json index e81be57..210b9a3 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,7 @@ { + "dependencies": { + "@nestjs/mapped-types": "*" + }, "name": "next-nest-template", "version": "0.0.0", "description": "Template repository for full-stack NestJS + NextJS projects", diff --git a/yarn.lock b/yarn.lock index aa0c21e..0fb07e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -289,6 +289,11 @@ path-to-regexp "3.2.0" tslib "2.6.2" +"@nestjs/mapped-types@*": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e" + integrity sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ== + "@nestjs/mapped-types@2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz#485d6b44e19779c98d04e52bd1d2bcc7001df0ea" @@ -648,6 +653,11 @@ "@types/node" "*" "@types/send" "*" +"@types/validator@^13.11.8": + version "13.12.2" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.2.tgz#760329e756e18a4aab82fc502b51ebdfebbe49f5" + integrity sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA== + "@typescript-eslint/eslint-plugin@^7.7.1": version "7.7.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz#50a9044e3e5fe76b22caf64fb7fc1f97614bdbfd" @@ -1027,6 +1037,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -1377,6 +1392,20 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +class-transformer@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + +class-validator@^0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== + dependencies: + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" + class-variance-authority@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522" @@ -1708,6 +1737,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3053,6 +3087,11 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +libphonenumber-js@^1.10.53: + version "1.11.14" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.14.tgz#d753524fd30e6433834a1464baf7efed4a06b593" + integrity sha512-sexvAfwcW1Lqws4zFp8heAtAEXbEDnvkYCEGzvOoMgZR7JhXo/IkE9MkkGACgBed5fWqh3ShBGnJBdDnU9N8EQ== + lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" @@ -4193,7 +4232,7 @@ streamsearch@^1.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@4.1.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.1.2: +string-width@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff" integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ== @@ -4202,6 +4241,24 @@ string-width@4.1.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2. is-fullwidth-code-point "^3.0.0" strip-ansi "^5.2.0" +string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.10: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" @@ -4707,6 +4764,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +validator@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -4846,15 +4908,24 @@ which@^2.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@7.0.0, wrap-ansi@^6.0.1, wrap-ansi@^6.2.0, wrap-ansi@^8.1.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"