From fec8192852384c02c9f5a092820a58eaaed41bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1muel=20Fekete?= Date: Mon, 22 Jul 2024 17:23:43 +0200 Subject: [PATCH] connect user and event entities (#30) --- .../migration.sql | 18 ---------- .../migration.sql | 32 ----------------- .../migration.sql | 6 ---- .../migration.sql | 35 +++++++++++-------- apps/backend/prisma/schema.prisma | 7 ++-- .../src/events/dto/create-event.dto.ts | 2 +- .../src/events/entities/event.entity.ts | 6 ++++ apps/backend/src/events/events.controller.ts | 29 ++++++++++----- apps/backend/src/events/events.service.ts | 30 +++++++++------- .../backend/src/users/entities/user.entity.ts | 9 ++++- 10 files changed, 78 insertions(+), 96 deletions(-) delete mode 100644 apps/backend/prisma/migrations/20240509172402_comment_out_user/migration.sql delete mode 100644 apps/backend/prisma/migrations/20240519193007_favorite_drink_model_reolaced_with_many_to_many/migration.sql delete mode 100644 apps/backend/prisma/migrations/20240706181116_added_gender_and_weight/migration.sql rename apps/backend/prisma/migrations/{20240509172109_init => 20240718171702_init}/migration.sql (72%) diff --git a/apps/backend/prisma/migrations/20240509172402_comment_out_user/migration.sql b/apps/backend/prisma/migrations/20240509172402_comment_out_user/migration.sql deleted file mode 100644 index 15ffcf8..0000000 --- a/apps/backend/prisma/migrations/20240509172402_comment_out_user/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `userId` on the `DrinkAction` table. All the data in the column will be lost. - - You are about to drop the column `ownerId` on the `Event` table. All the data in the column will be lost. - -*/ --- DropForeignKey -ALTER TABLE "DrinkAction" DROP CONSTRAINT "DrinkAction_userId_fkey"; - --- DropForeignKey -ALTER TABLE "Event" DROP CONSTRAINT "Event_ownerId_fkey"; - --- AlterTable -ALTER TABLE "DrinkAction" DROP COLUMN "userId"; - --- AlterTable -ALTER TABLE "Event" DROP COLUMN "ownerId"; diff --git a/apps/backend/prisma/migrations/20240519193007_favorite_drink_model_reolaced_with_many_to_many/migration.sql b/apps/backend/prisma/migrations/20240519193007_favorite_drink_model_reolaced_with_many_to_many/migration.sql deleted file mode 100644 index ab670d9..0000000 --- a/apps/backend/prisma/migrations/20240519193007_favorite_drink_model_reolaced_with_many_to_many/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ -/* - Warnings: - - - You are about to drop the `FavouriteDrink` table. If the table is not empty, all the data it contains will be lost. - -*/ --- DropForeignKey -ALTER TABLE "FavouriteDrink" DROP CONSTRAINT "FavouriteDrink_drinkId_fkey"; - --- DropForeignKey -ALTER TABLE "FavouriteDrink" DROP CONSTRAINT "FavouriteDrink_userId_fkey"; - --- DropTable -DROP TABLE "FavouriteDrink"; - --- CreateTable -CREATE TABLE "_DrinkToUser" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL -); - --- CreateIndex -CREATE UNIQUE INDEX "_DrinkToUser_AB_unique" ON "_DrinkToUser"("A", "B"); - --- CreateIndex -CREATE INDEX "_DrinkToUser_B_index" ON "_DrinkToUser"("B"); - --- AddForeignKey -ALTER TABLE "_DrinkToUser" ADD CONSTRAINT "_DrinkToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Drink"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_DrinkToUser" ADD CONSTRAINT "_DrinkToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("authSchId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/backend/prisma/migrations/20240706181116_added_gender_and_weight/migration.sql b/apps/backend/prisma/migrations/20240706181116_added_gender_and_weight/migration.sql deleted file mode 100644 index 929bab7..0000000 --- a/apps/backend/prisma/migrations/20240706181116_added_gender_and_weight/migration.sql +++ /dev/null @@ -1,6 +0,0 @@ --- CreateEnum -CREATE TYPE "Gender" AS ENUM ('Male', 'Female'); - --- AlterTable -ALTER TABLE "User" ADD COLUMN "gender" "Gender", -ADD COLUMN "weight" DOUBLE PRECISION; diff --git a/apps/backend/prisma/migrations/20240509172109_init/migration.sql b/apps/backend/prisma/migrations/20240718171702_init/migration.sql similarity index 72% rename from apps/backend/prisma/migrations/20240509172109_init/migration.sql rename to apps/backend/prisma/migrations/20240718171702_init/migration.sql index c59bf87..1097ea7 100644 --- a/apps/backend/prisma/migrations/20240509172109_init/migration.sql +++ b/apps/backend/prisma/migrations/20240718171702_init/migration.sql @@ -1,12 +1,18 @@ -- CreateEnum CREATE TYPE "DrinkType" AS ENUM ('BEER', 'WINE', 'SPIRIT', 'COCKTAIL'); +-- CreateEnum +CREATE TYPE "Gender" AS ENUM ('Male', 'Female'); + -- CreateTable CREATE TABLE "User" ( "authSchId" TEXT NOT NULL, "email" TEXT NOT NULL, "firstName" TEXT NOT NULL, "lastName" TEXT NOT NULL, + "gender" "Gender", + "weight" DOUBLE PRECISION, + "isAdmin" BOOLEAN NOT NULL DEFAULT false, "profilePictureUrl" TEXT, CONSTRAINT "User_pkey" PRIMARY KEY ("authSchId") @@ -25,15 +31,6 @@ CREATE TABLE "Drink" ( CONSTRAINT "Drink_pkey" PRIMARY KEY ("id") ); --- CreateTable -CREATE TABLE "FavouriteDrink" ( - "drinkId" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "order" INTEGER NOT NULL, - - CONSTRAINT "FavouriteDrink_pkey" PRIMARY KEY ("drinkId","userId") -); - -- CreateTable CREATE TABLE "Event" ( "id" TEXT NOT NULL, @@ -55,19 +52,24 @@ CREATE TABLE "DrinkAction" ( "milliliter" INTEGER NOT NULL, "drinkId" TEXT NOT NULL, "eventId" TEXT NOT NULL, - "userId" TEXT NOT NULL, CONSTRAINT "DrinkAction_pkey" PRIMARY KEY ("id") ); +-- CreateTable +CREATE TABLE "_DrinkToUser" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + -- CreateIndex CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); --- AddForeignKey -ALTER TABLE "FavouriteDrink" ADD CONSTRAINT "FavouriteDrink_drinkId_fkey" FOREIGN KEY ("drinkId") REFERENCES "Drink"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +-- CreateIndex +CREATE UNIQUE INDEX "_DrinkToUser_AB_unique" ON "_DrinkToUser"("A", "B"); --- AddForeignKey -ALTER TABLE "FavouriteDrink" ADD CONSTRAINT "FavouriteDrink_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("authSchId") ON DELETE RESTRICT ON UPDATE CASCADE; +-- CreateIndex +CREATE INDEX "_DrinkToUser_B_index" ON "_DrinkToUser"("B"); -- AddForeignKey ALTER TABLE "Event" ADD CONSTRAINT "Event_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("authSchId") ON DELETE RESTRICT ON UPDATE CASCADE; @@ -79,4 +81,7 @@ ALTER TABLE "DrinkAction" ADD CONSTRAINT "DrinkAction_drinkId_fkey" FOREIGN KEY ALTER TABLE "DrinkAction" ADD CONSTRAINT "DrinkAction_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event"("id") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "DrinkAction" ADD CONSTRAINT "DrinkAction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("authSchId") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "_DrinkToUser" ADD CONSTRAINT "_DrinkToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Drink"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_DrinkToUser" ADD CONSTRAINT "_DrinkToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("authSchId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma index 4e5117a..f07e2a9 100644 --- a/apps/backend/prisma/schema.prisma +++ b/apps/backend/prisma/schema.prisma @@ -33,8 +33,9 @@ model User { lastName String gender Gender? weight Float? + isAdmin Boolean @default(false) profilePictureUrl String? - // ownedEvents Event[] + ownedEvents Event[] // drinkActions DrinkAction[] favouriteDrinks Drink[] } @@ -54,8 +55,8 @@ model Drink { model Event { id String @id @default(uuid()) - // ownerId String - // owner User @relation(fields: [ownerId], references: [authSchId]) + ownerId String + owner User @relation(fields: [ownerId], references: [authSchId]) name String location String startDate DateTime diff --git a/apps/backend/src/events/dto/create-event.dto.ts b/apps/backend/src/events/dto/create-event.dto.ts index 5758e42..a7eba2c 100644 --- a/apps/backend/src/events/dto/create-event.dto.ts +++ b/apps/backend/src/events/dto/create-event.dto.ts @@ -2,4 +2,4 @@ import { OmitType } from '@nestjs/swagger'; import { Event } from '../entities/event.entity'; -export class CreateEventDto extends OmitType(Event, ['id', 'createdAt']) {} +export class CreateEventDto extends OmitType(Event, ['id', 'createdAt', 'ownerId']) {} diff --git a/apps/backend/src/events/entities/event.entity.ts b/apps/backend/src/events/entities/event.entity.ts index 84cdf9e..4fb6d3b 100644 --- a/apps/backend/src/events/entities/event.entity.ts +++ b/apps/backend/src/events/entities/event.entity.ts @@ -22,6 +22,12 @@ export class Event { @IsNotEmpty() location: string; + /** + * Owner's ID. + */ + @IsUUID() + ownerId: string; + /** * Date and time when the event starts. * @example '2022-01-01T22:30:00.000Z' diff --git a/apps/backend/src/events/events.controller.ts b/apps/backend/src/events/events.controller.ts index 3695e6c..240331a 100644 --- a/apps/backend/src/events/events.controller.ts +++ b/apps/backend/src/events/events.controller.ts @@ -1,5 +1,8 @@ -import { Body, Controller, Delete, Get, Param, ParseUUIDPipe, Patch, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { CurrentUser } from '@kir-dev/passport-authsch'; +import { Body, Controller, Delete, Get, Param, ParseUUIDPipe, Patch, Post, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { User } from 'src/users/entities/user.entity'; import { CreateEventDto } from './dto/create-event.dto'; import { UpdateEventDto } from './dto/update-event.dto'; @@ -12,8 +15,10 @@ export class EventsController { constructor(private readonly eventsService: EventsService) {} @Post() - async create(@Body() data: CreateEventDto): Promise { - return this.eventsService.create(data); + @UseGuards(AuthGuard('jwt')) + @ApiBearerAuth() + async create(@Body() data: CreateEventDto, @CurrentUser() user: User): Promise { + return this.eventsService.create(data, user.authSchId); } @Get() @@ -27,12 +32,20 @@ export class EventsController { } @Patch(':id') - async update(@Param('id', new ParseUUIDPipe()) id: string, @Body() data: UpdateEventDto): Promise { - return this.eventsService.update(id, data); + @UseGuards(AuthGuard('jwt')) + @ApiBearerAuth() + async update( + @Param('id', new ParseUUIDPipe()) id: string, + @Body() data: UpdateEventDto, + @CurrentUser() user: User + ): Promise { + return this.eventsService.update(id, data, user); } @Delete(':id') - async remove(@Param('id', new ParseUUIDPipe()) id: string): Promise { - return this.eventsService.remove(id); + @UseGuards(AuthGuard('jwt')) + @ApiBearerAuth() + async remove(@Param('id', new ParseUUIDPipe()) id: string, @CurrentUser() user: User): Promise { + return this.eventsService.remove(id, user); } } diff --git a/apps/backend/src/events/events.service.ts b/apps/backend/src/events/events.service.ts index 652fefd..5cf323b 100644 --- a/apps/backend/src/events/events.service.ts +++ b/apps/backend/src/events/events.service.ts @@ -1,6 +1,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { Event } from '@prisma/client'; import { PrismaService } from 'nestjs-prisma'; +import { User } from 'src/users/entities/user.entity'; import { CreateEventDto } from './dto/create-event.dto'; import { UpdateEventDto } from './dto/update-event.dto'; @@ -9,8 +10,13 @@ import { UpdateEventDto } from './dto/update-event.dto'; export class EventsService { constructor(readonly prisma: PrismaService) {} - async create(data: CreateEventDto): Promise { - return await this.prisma.event.create({ data }); + async create(data: CreateEventDto, userId: string): Promise { + return await this.prisma.event.create({ + data: { + ...data, + ownerId: userId, + }, + }); } async findAll(): Promise { @@ -25,19 +31,19 @@ export class EventsService { return event; } - async update(id: string, data: UpdateEventDto): Promise { - try { - return await this.prisma.event.update({ where: { id }, data }); - } catch { - throw new NotFoundException('Event not found'); + async update(id: string, data: UpdateEventDto, user: User): Promise { + const event = await this.prisma.event.findUnique({ where: { id } }); + if (!event || (event.ownerId !== user.authSchId && !user.isAdmin)) { + throw new NotFoundException("Event not found or you don't have permission to update it"); } + return await this.prisma.event.update({ where: { id }, data }); } - async remove(id: string): Promise { - try { - return await this.prisma.event.delete({ where: { id } }); - } catch { - throw new NotFoundException('Event not found'); + async remove(id: string, user: User): Promise { + const event = await this.prisma.event.findUnique({ where: { id } }); + if (!event || (event.ownerId !== user.authSchId && !user.isAdmin)) { + throw new NotFoundException("Event not found or you don't have permission to delete it"); } + return await this.prisma.event.delete({ where: { id } }); } } diff --git a/apps/backend/src/users/entities/user.entity.ts b/apps/backend/src/users/entities/user.entity.ts index 5a8a2d7..184b447 100644 --- a/apps/backend/src/users/entities/user.entity.ts +++ b/apps/backend/src/users/entities/user.entity.ts @@ -1,5 +1,6 @@ import { Gender } from '@prisma/client'; -import { IsNotEmpty, IsOptional, IsString, IsUUID, Min } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, Min } from 'class-validator'; + export class User { @IsUUID() authSchId: string; @@ -43,6 +44,12 @@ export class User { @Min(0) weight?: number; + /** + * Is the user an administrator? + */ + @IsBoolean() + isAdmin: boolean; + @IsOptional() profilePictureUrl?: string; }